From 8d36f2f9d9973e4b6c631d9bd7d092485b5f0830 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Thu, 5 Mar 2026 21:11:38 +0100 Subject: [PATCH 1/4] Git - adopt the new package to use copy-on-write for the worktree include files --- extensions/git/package-lock.json | 27 ++++++++++ extensions/git/package.json | 3 ++ extensions/git/src/repository.ts | 90 +++++++++++++++++++++++++++----- 3 files changed, 106 insertions(+), 14 deletions(-) diff --git a/extensions/git/package-lock.json b/extensions/git/package-lock.json index b552ce9fa5b3f..839f092a808f4 100644 --- a/extensions/git/package-lock.json +++ b/extensions/git/package-lock.json @@ -26,6 +26,9 @@ }, "engines": { "vscode": "^1.5.0" + }, + "optionalDependencies": { + "@vscode/fs-copyfile": "1.1.1" } }, "node_modules/@joaomoreno/unique-names-generator": { @@ -218,6 +221,23 @@ "vscode": "^1.75.0" } }, + "node_modules/@vscode/fs-copyfile": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@vscode/fs-copyfile/-/fs-copyfile-1.1.1.tgz", + "integrity": "sha512-eK/ahMOhAixliN1vXxhG7zfuatSNQi/HX6KUX5GHZqCvMT7E1oBmYy7ZrJlBA7H9MSMaS36MewLZeF80AOCTwQ==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">=22.6.0" + } + }, "node_modules/byline": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", @@ -274,6 +294,13 @@ "node": ">=16" } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT", + "optional": true + }, "node_modules/peek-readable": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz", diff --git a/extensions/git/package.json b/extensions/git/package.json index 1fbac49569f9f..39949d929c180 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -4352,6 +4352,9 @@ "vscode-uri": "^2.0.0", "which": "4.0.0" }, + "optionalDependencies": { + "@vscode/fs-copyfile": "1.1.1" + }, "devDependencies": { "@types/byline": "4.2.31", "@types/mocha": "^10.0.10", diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index bd6b6a5c7ff66..88404137c6e05 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -9,6 +9,7 @@ import * as fs from 'fs'; import * as fsPromises from 'fs/promises'; import * as path from 'path'; import picomatch from 'picomatch'; +import type { CpOptions } from '@vscode/fs-copyfile'; import { CancellationError, CancellationToken, CancellationTokenSource, Command, commands, Disposable, Event, EventEmitter, ExcludeSettingOptions, FileDecoration, l10n, LogLevel, LogOutputChannel, Memento, ProgressLocation, ProgressOptions, RelativePattern, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, TabInputNotebookDiff, TabInputTextDiff, TabInputTextMultiDiff, ThemeColor, ThemeIcon, Uri, window, workspace, WorkspaceEdit } from 'vscode'; import { ActionButton } from './actionButton'; import { ApiRepository } from './api/api1'; @@ -25,7 +26,7 @@ import { IPushErrorHandlerRegistry } from './pushError'; import { IRemoteSourcePublisherRegistry } from './remotePublisher'; import { StatusBarCommands } from './statusbar'; import { toGitUri } from './uri'; -import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, getCommitShortHash, IDisposable, isCopilotWorktreeFolder, isDescendant, isLinuxSnap, isRemote, isWindows, Limiter, onceEvent, pathEquals, relativePath } from './util'; +import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, getCommitShortHash, IDisposable, isCopilotWorktreeFolder, isDescendant, isLinuxSnap, isMacintosh, isRemote, isWindows, Limiter, onceEvent, pathEquals, relativePath } from './util'; import { IFileWatcher, watch } from './watch'; import { ISourceControlHistoryItemDetailsProviderRegistry } from './historyItemDetailsProvider'; import { GitArtifactProvider } from './artifactProvider'; @@ -1930,13 +1931,33 @@ export class Repository implements Disposable { gitIgnoredFiles.delete(uri.fsPath); } - // Add the folder paths for git ignored files + // Compute the base directory for each glob pattern (the fixed + // prefix before any wildcard characters). This will be used to + // optimize the upward traversal when adding parent directories. + const filePatternBases = new Set(); + for (const pattern of worktreeIncludeFiles) { + const segments = pattern.split(/[\/\\]/); + const fixedSegments: string[] = []; + for (const seg of segments) { + if (/[*?{}[\]]/.test(seg)) { + break; + } + fixedSegments.push(seg); + } + filePatternBases.add(path.join(this.root, ...fixedSegments)); + } + + // Add the folder paths for git ignored files, walking + // up only to the nearest file pattern base directory. const gitIgnoredPaths = new Set(gitIgnoredFiles); for (const filePath of gitIgnoredFiles) { let dir = path.dirname(filePath); - while (dir !== this.root && !gitIgnoredFiles.has(dir)) { + while (dir !== this.root && !gitIgnoredPaths.has(dir)) { gitIgnoredPaths.add(dir); + if (filePatternBases.has(dir)) { + break; + } dir = path.dirname(dir); } } @@ -1950,15 +1971,34 @@ export class Repository implements Disposable { return; } + // On macOS, we can use the native fclonefileat syscall to perform a + // copy-on-write clone of entire directory trees in a single syscall. + // This is nearly instant on APFS volumes. + let nativeClone: ((src: string, dest: string, mode?: number) => Promise) | undefined = undefined; + let nativeCp: ((src: string, dest: string, options?: CpOptions) => Promise) | undefined = undefined; + if (isMacintosh) { + try { + const fsModule = await import('@vscode/fs-copyfile'); + nativeClone = fsModule.copyFile; + nativeCp = fsModule.cp; + this.logger.info(`[Repository][_copyWorktreeIncludeFiles] Native @vscode/fs-copyfile module loaded.`); + } catch (err) { + this.logger.warn(`[Repository][_copyWorktreeIncludeFiles] Failed to load @vscode/fs-copyfile: ${err}`); + } + } + try { - // Find minimal set of paths (folders and files) to copy. - // The goal is to reduce the number of copy operations - // needed. + // Find minimal set of paths (folders and files) to copy. Keep only topmost + // paths — if a directory is already in the set, all its descendants are + // implicitly included and don't need separate entries. + let lastTopmost: string | undefined; const pathsToCopy = new Set(); - for (const filePath of gitIgnoredPaths) { - const relativePath = path.relative(this.root, filePath); - const firstSegment = relativePath.split(path.sep)[0]; - pathsToCopy.add(path.join(this.root, firstSegment)); + for (const p of Array.from(gitIgnoredPaths).sort()) { + if (lastTopmost && (p === lastTopmost || p.startsWith(lastTopmost + path.sep))) { + continue; + } + pathsToCopy.add(p); + lastTopmost = p; } const startTime = Date.now(); @@ -1966,19 +2006,41 @@ export class Repository implements Disposable { const files = Array.from(pathsToCopy); // Copy files - const results = await Promise.allSettled(files.map(sourceFile => + const results = await Promise.allSettled(files.map(sourceFile => { limiter.queue(async () => { const targetFile = path.join(worktreePath, relativePath(this.root, sourceFile)); await fsPromises.mkdir(path.dirname(targetFile), { recursive: true }); + + if (nativeClone) { + // Try to clone the entire tree atomically using a single fclonefileat + // syscall. This clones the whole directory in one operation and is nearly + // instant. + try { + await nativeClone(sourceFile, targetFile, fs.constants.COPYFILE_FICLONE_FORCE); + return; + } catch { } + } + + if (nativeCp) { + // Use the native cp implementation + await nativeCp(sourceFile, targetFile, { + force: true, + mode: fs.constants.COPYFILE_FICLONE, + recursive: true, + verbatimSymlinks: true + }); + return; + } + + // Fallback to regular copy await fsPromises.cp(sourceFile, targetFile, { - filter: src => gitIgnoredPaths.has(src), force: true, mode: fs.constants.COPYFILE_FICLONE, recursive: true, verbatimSymlinks: true }); - }) - )); + }); + })); // Log any failed operations const failedOperations = results.filter(r => r.status === 'rejected'); From 5bfd5ae2f06a77fc0c03200754c4e01ce782b49d Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 6 Mar 2026 10:05:08 +0100 Subject: [PATCH 2/4] Adopt the latest package and clean up the code --- extensions/git/package-lock.json | 8 ++-- extensions/git/package.json | 2 +- extensions/git/src/repository.ts | 73 +++++++++++++------------------- 3 files changed, 34 insertions(+), 49 deletions(-) diff --git a/extensions/git/package-lock.json b/extensions/git/package-lock.json index 839f092a808f4..0eabadb9fec97 100644 --- a/extensions/git/package-lock.json +++ b/extensions/git/package-lock.json @@ -28,7 +28,7 @@ "vscode": "^1.5.0" }, "optionalDependencies": { - "@vscode/fs-copyfile": "1.1.1" + "@vscode/fs-copyfile": "1.2.0" } }, "node_modules/@joaomoreno/unique-names-generator": { @@ -222,9 +222,9 @@ } }, "node_modules/@vscode/fs-copyfile": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@vscode/fs-copyfile/-/fs-copyfile-1.1.1.tgz", - "integrity": "sha512-eK/ahMOhAixliN1vXxhG7zfuatSNQi/HX6KUX5GHZqCvMT7E1oBmYy7ZrJlBA7H9MSMaS36MewLZeF80AOCTwQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vscode/fs-copyfile/-/fs-copyfile-1.2.0.tgz", + "integrity": "sha512-QbAuG1JvGWaJKdfMAdcRVf4qEGPek58bZS+q4xqwulK2ppCuMt2UFffMiWsj8/McU7ZVFW7x3A1e3HQdjTMo6A==", "hasInstallScript": true, "license": "MIT", "optional": true, diff --git a/extensions/git/package.json b/extensions/git/package.json index 39949d929c180..5b39f92a41875 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -4353,7 +4353,7 @@ "which": "4.0.0" }, "optionalDependencies": { - "@vscode/fs-copyfile": "1.1.1" + "@vscode/fs-copyfile": "1.2.0" }, "devDependencies": { "@types/byline": "4.2.31", diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 88404137c6e05..4f7b5f3066989 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -9,7 +9,6 @@ import * as fs from 'fs'; import * as fsPromises from 'fs/promises'; import * as path from 'path'; import picomatch from 'picomatch'; -import type { CpOptions } from '@vscode/fs-copyfile'; import { CancellationError, CancellationToken, CancellationTokenSource, Command, commands, Disposable, Event, EventEmitter, ExcludeSettingOptions, FileDecoration, l10n, LogLevel, LogOutputChannel, Memento, ProgressLocation, ProgressOptions, RelativePattern, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, TabInputNotebookDiff, TabInputTextDiff, TabInputTextMultiDiff, ThemeColor, ThemeIcon, Uri, window, workspace, WorkspaceEdit } from 'vscode'; import { ActionButton } from './actionButton'; import { ApiRepository } from './api/api1'; @@ -1962,83 +1961,69 @@ export class Repository implements Disposable { } } - return gitIgnoredPaths; + // Find minimal set of paths (folders and files) to copy. Keep only topmost + // paths — if a directory is already in the set, all its descendants are + // implicitly included and don't need separate entries. + let lastTopmost: string | undefined; + const pathsToCopy = new Set(); + for (const p of Array.from(gitIgnoredPaths).sort()) { + if (lastTopmost && (p === lastTopmost || p.startsWith(lastTopmost + path.sep))) { + continue; + } + pathsToCopy.add(p); + lastTopmost = p; + } + + return pathsToCopy; } private async _copyWorktreeIncludeFiles(worktreePath: string): Promise { - const gitIgnoredPaths = await this._getWorktreeIncludePaths(); - if (gitIgnoredPaths.size === 0) { + const worktreeIncludePaths = await this._getWorktreeIncludePaths(); + if (worktreeIncludePaths.size === 0) { return; } // On macOS, we can use the native fclonefileat syscall to perform a // copy-on-write clone of entire directory trees in a single syscall. // This is nearly instant on APFS volumes. - let nativeClone: ((src: string, dest: string, mode?: number) => Promise) | undefined = undefined; - let nativeCp: ((src: string, dest: string, options?: CpOptions) => Promise) | undefined = undefined; + let nativeCp: ((src: string, dest: string, options?: fs.CopyOptions) => Promise) | undefined = undefined; if (isMacintosh) { try { - const fsModule = await import('@vscode/fs-copyfile'); - nativeClone = fsModule.copyFile; - nativeCp = fsModule.cp; + nativeCp = (await import('@vscode/fs-copyfile')).cp; this.logger.info(`[Repository][_copyWorktreeIncludeFiles] Native @vscode/fs-copyfile module loaded.`); } catch (err) { - this.logger.warn(`[Repository][_copyWorktreeIncludeFiles] Failed to load @vscode/fs-copyfile: ${err}`); + const error = err instanceof Error ? err.message : String(err); + this.logger.warn(`[Repository][_copyWorktreeIncludeFiles] Failed to load @vscode/fs-copyfile: ${error}`); } } try { - // Find minimal set of paths (folders and files) to copy. Keep only topmost - // paths — if a directory is already in the set, all its descendants are - // implicitly included and don't need separate entries. - let lastTopmost: string | undefined; - const pathsToCopy = new Set(); - for (const p of Array.from(gitIgnoredPaths).sort()) { - if (lastTopmost && (p === lastTopmost || p.startsWith(lastTopmost + path.sep))) { - continue; - } - pathsToCopy.add(p); - lastTopmost = p; - } - const startTime = Date.now(); const limiter = new Limiter(15); - const files = Array.from(pathsToCopy); + const files = Array.from(worktreeIncludePaths); // Copy files const results = await Promise.allSettled(files.map(sourceFile => { - limiter.queue(async () => { + return limiter.queue(async () => { const targetFile = path.join(worktreePath, relativePath(this.root, sourceFile)); await fsPromises.mkdir(path.dirname(targetFile), { recursive: true }); - if (nativeClone) { - // Try to clone the entire tree atomically using a single fclonefileat - // syscall. This clones the whole directory in one operation and is nearly - // instant. - try { - await nativeClone(sourceFile, targetFile, fs.constants.COPYFILE_FICLONE_FORCE); - return; - } catch { } - } - if (nativeCp) { // Use the native cp implementation await nativeCp(sourceFile, targetFile, { + force: true, + recursive: true, + verbatimSymlinks: true + }); + } else { + // Fallback to regular copy + await fsPromises.cp(sourceFile, targetFile, { force: true, mode: fs.constants.COPYFILE_FICLONE, recursive: true, verbatimSymlinks: true }); - return; } - - // Fallback to regular copy - await fsPromises.cp(sourceFile, targetFile, { - force: true, - mode: fs.constants.COPYFILE_FICLONE, - recursive: true, - verbatimSymlinks: true - }); }); })); From 83a0d74c39a7bfe50d9b5f84a355f91df6d70023 Mon Sep 17 00:00:00 2001 From: deepak1556 Date: Sat, 7 Mar 2026 19:47:21 +0900 Subject: [PATCH 3/4] chore: use performance timer --- extensions/git/src/repository.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 4f7b5f3066989..16530c1e75c82 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -1998,7 +1998,7 @@ export class Repository implements Disposable { } try { - const startTime = Date.now(); + const startTime = performance.now(); const limiter = new Limiter(15); const files = Array.from(worktreeIncludePaths); @@ -2008,6 +2008,7 @@ export class Repository implements Disposable { const targetFile = path.join(worktreePath, relativePath(this.root, sourceFile)); await fsPromises.mkdir(path.dirname(targetFile), { recursive: true }); + const cpStart = performance.now(); if (nativeCp) { // Use the native cp implementation await nativeCp(sourceFile, targetFile, { @@ -2015,6 +2016,7 @@ export class Repository implements Disposable { recursive: true, verbatimSymlinks: true }); + this.logger.trace(`[Repository][_copyWorktreeIncludeFiles] nativeCp: ${sourceFile} -> ${targetFile} [${(performance.now() - cpStart).toFixed(2)}ms]`); } else { // Fallback to regular copy await fsPromises.cp(sourceFile, targetFile, { @@ -2023,13 +2025,14 @@ export class Repository implements Disposable { recursive: true, verbatimSymlinks: true }); + this.logger.trace(`[Repository][_copyWorktreeIncludeFiles] fsPromises.cp: ${sourceFile} -> ${targetFile} [${(performance.now() - cpStart).toFixed(2)}ms]`); } }); })); // Log any failed operations const failedOperations = results.filter(r => r.status === 'rejected'); - this.logger.info(`[Repository][_copyWorktreeIncludeFiles] Copied ${files.length - failedOperations.length}/${files.length} folder(s)/file(s) to worktree. [${Date.now() - startTime}ms]`); + this.logger.info(`[Repository][_copyWorktreeIncludeFiles] Copied ${files.length - failedOperations.length}/${files.length} folder(s)/file(s) to worktree. [${(performance.now() - startTime).toFixed(2)}ms]`); if (failedOperations.length > 0) { window.showWarningMessage(l10n.t('Failed to copy {0} folder(s)/file(s) to the worktree.', failedOperations.length)); From e18ac949619632257fb5f4e4d79d4d5638604178 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 9 Mar 2026 16:08:56 +0100 Subject: [PATCH 4/4] Refactor code --- extensions/git/package-lock.json | 17 +++++--------- extensions/git/package.json | 4 +--- extensions/git/src/repository.ts | 38 +++----------------------------- 3 files changed, 9 insertions(+), 50 deletions(-) diff --git a/extensions/git/package-lock.json b/extensions/git/package-lock.json index 0eabadb9fec97..6353cbc2753cd 100644 --- a/extensions/git/package-lock.json +++ b/extensions/git/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@joaomoreno/unique-names-generator": "^5.2.0", "@vscode/extension-telemetry": "^0.9.8", + "@vscode/fs-copyfile": "2.0.0", "byline": "^5.0.0", "file-type": "16.5.4", "picomatch": "2.3.1", @@ -26,9 +27,6 @@ }, "engines": { "vscode": "^1.5.0" - }, - "optionalDependencies": { - "@vscode/fs-copyfile": "1.2.0" } }, "node_modules/@joaomoreno/unique-names-generator": { @@ -222,15 +220,11 @@ } }, "node_modules/@vscode/fs-copyfile": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@vscode/fs-copyfile/-/fs-copyfile-1.2.0.tgz", - "integrity": "sha512-QbAuG1JvGWaJKdfMAdcRVf4qEGPek58bZS+q4xqwulK2ppCuMt2UFffMiWsj8/McU7ZVFW7x3A1e3HQdjTMo6A==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@vscode/fs-copyfile/-/fs-copyfile-2.0.0.tgz", + "integrity": "sha512-ARb4+9rN905WjJtQ2mSBG/q4pjJkSRun/MkfCeRkk7h/5J8w4vd18NCePFJ/ZucIwXx/7mr9T6nz9Vtt1tk7hg==", "hasInstallScript": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], "dependencies": { "node-addon-api": "^7.0.0" }, @@ -298,8 +292,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/peek-readable": { "version": "4.1.0", diff --git a/extensions/git/package.json b/extensions/git/package.json index 5b39f92a41875..f0e49309944a7 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -4346,15 +4346,13 @@ "dependencies": { "@joaomoreno/unique-names-generator": "^5.2.0", "@vscode/extension-telemetry": "^0.9.8", + "@vscode/fs-copyfile": "2.0.0", "byline": "^5.0.0", "file-type": "16.5.4", "picomatch": "2.3.1", "vscode-uri": "^2.0.0", "which": "4.0.0" }, - "optionalDependencies": { - "@vscode/fs-copyfile": "1.2.0" - }, "devDependencies": { "@types/byline": "4.2.31", "@types/mocha": "^10.0.10", diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 16530c1e75c82..53db3e48495da 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { cp } from '@vscode/fs-copyfile'; import TelemetryReporter from '@vscode/extension-telemetry'; import { uniqueNamesGenerator, adjectives, animals, colors, NumberDictionary } from '@joaomoreno/unique-names-generator'; import * as fs from 'fs'; @@ -25,7 +26,7 @@ import { IPushErrorHandlerRegistry } from './pushError'; import { IRemoteSourcePublisherRegistry } from './remotePublisher'; import { StatusBarCommands } from './statusbar'; import { toGitUri } from './uri'; -import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, getCommitShortHash, IDisposable, isCopilotWorktreeFolder, isDescendant, isLinuxSnap, isMacintosh, isRemote, isWindows, Limiter, onceEvent, pathEquals, relativePath } from './util'; +import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, getCommitShortHash, IDisposable, isCopilotWorktreeFolder, isDescendant, isLinuxSnap, isRemote, isWindows, Limiter, onceEvent, pathEquals, relativePath } from './util'; import { IFileWatcher, watch } from './watch'; import { ISourceControlHistoryItemDetailsProviderRegistry } from './historyItemDetailsProvider'; import { GitArtifactProvider } from './artifactProvider'; @@ -1983,20 +1984,6 @@ export class Repository implements Disposable { return; } - // On macOS, we can use the native fclonefileat syscall to perform a - // copy-on-write clone of entire directory trees in a single syscall. - // This is nearly instant on APFS volumes. - let nativeCp: ((src: string, dest: string, options?: fs.CopyOptions) => Promise) | undefined = undefined; - if (isMacintosh) { - try { - nativeCp = (await import('@vscode/fs-copyfile')).cp; - this.logger.info(`[Repository][_copyWorktreeIncludeFiles] Native @vscode/fs-copyfile module loaded.`); - } catch (err) { - const error = err instanceof Error ? err.message : String(err); - this.logger.warn(`[Repository][_copyWorktreeIncludeFiles] Failed to load @vscode/fs-copyfile: ${error}`); - } - } - try { const startTime = performance.now(); const limiter = new Limiter(15); @@ -2007,26 +1994,7 @@ export class Repository implements Disposable { return limiter.queue(async () => { const targetFile = path.join(worktreePath, relativePath(this.root, sourceFile)); await fsPromises.mkdir(path.dirname(targetFile), { recursive: true }); - - const cpStart = performance.now(); - if (nativeCp) { - // Use the native cp implementation - await nativeCp(sourceFile, targetFile, { - force: true, - recursive: true, - verbatimSymlinks: true - }); - this.logger.trace(`[Repository][_copyWorktreeIncludeFiles] nativeCp: ${sourceFile} -> ${targetFile} [${(performance.now() - cpStart).toFixed(2)}ms]`); - } else { - // Fallback to regular copy - await fsPromises.cp(sourceFile, targetFile, { - force: true, - mode: fs.constants.COPYFILE_FICLONE, - recursive: true, - verbatimSymlinks: true - }); - this.logger.trace(`[Repository][_copyWorktreeIncludeFiles] fsPromises.cp: ${sourceFile} -> ${targetFile} [${(performance.now() - cpStart).toFixed(2)}ms]`); - } + await cp(sourceFile, targetFile, { force: true, recursive: true, verbatimSymlinks: true }); }); }));