diff --git a/packages/core/src/worktree/__tests__/worktree-include.spec.ts b/packages/core/src/worktree/__tests__/worktree-include.spec.ts index 88069b95da..743430b38f 100644 --- a/packages/core/src/worktree/__tests__/worktree-include.spec.ts +++ b/packages/core/src/worktree/__tests__/worktree-include.spec.ts @@ -302,5 +302,99 @@ describe("WorktreeIncludeService", () => { expect(result).toContain("node_modules") }) + + describe("useHardLinks", () => { + it("should hard-link single files when useHardLinks is true", async () => { + await fs.writeFile(path.join(sourceDir, ".worktreeinclude"), ".env.local") + await fs.writeFile(path.join(sourceDir, ".gitignore"), ".env.local") + await fs.writeFile(path.join(sourceDir, ".env.local"), "LOCAL_VAR=value") + + const result = await service.copyWorktreeIncludeFiles(sourceDir, targetDir, undefined, true) + + expect(result).toContain(".env.local") + const copiedContent = await fs.readFile(path.join(targetDir, ".env.local"), "utf-8") + expect(copiedContent).toBe("LOCAL_VAR=value") + + // Verify it's a hard link (same inode) + const sourceStats = await fs.stat(path.join(sourceDir, ".env.local")) + const targetStats = await fs.stat(path.join(targetDir, ".env.local")) + expect(targetStats.ino).toBe(sourceStats.ino) + }) + + it("should hard-link directory contents recursively when useHardLinks is true", async () => { + await fs.writeFile(path.join(sourceDir, ".worktreeinclude"), "node_modules") + await fs.writeFile(path.join(sourceDir, ".gitignore"), "node_modules") + await fs.mkdir(path.join(sourceDir, "node_modules", "pkg"), { recursive: true }) + await fs.writeFile(path.join(sourceDir, "node_modules", "pkg", "index.js"), "module.exports = {}") + await fs.writeFile(path.join(sourceDir, "node_modules", "test.txt"), "test") + + const result = await service.copyWorktreeIncludeFiles(sourceDir, targetDir, undefined, true) + + expect(result).toContain("node_modules") + + // Verify file contents + const copiedContent = await fs.readFile( + path.join(targetDir, "node_modules", "pkg", "index.js"), + "utf-8", + ) + expect(copiedContent).toBe("module.exports = {}") + + // Verify hard links (same inode) + const sourceStats = await fs.stat(path.join(sourceDir, "node_modules", "test.txt")) + const targetStats = await fs.stat(path.join(targetDir, "node_modules", "test.txt")) + expect(targetStats.ino).toBe(sourceStats.ino) + }) + + it("should fall back to copy when hard link fails (e.g., cross-device)", async () => { + // Even if hard linking were to fail, the fallback ensures files are still copied + await fs.writeFile(path.join(sourceDir, ".worktreeinclude"), ".env.local") + await fs.writeFile(path.join(sourceDir, ".gitignore"), ".env.local") + await fs.writeFile(path.join(sourceDir, ".env.local"), "LOCAL_VAR=value") + + // We can't easily simulate cross-device in a unit test, but we can verify + // that when useHardLinks is true, the file is still accessible in the target + const result = await service.copyWorktreeIncludeFiles(sourceDir, targetDir, undefined, true) + + expect(result).toContain(".env.local") + const copiedContent = await fs.readFile(path.join(targetDir, ".env.local"), "utf-8") + expect(copiedContent).toBe("LOCAL_VAR=value") + }) + + it("should perform regular copies when useHardLinks is false", async () => { + await fs.writeFile(path.join(sourceDir, ".worktreeinclude"), ".env.local") + await fs.writeFile(path.join(sourceDir, ".gitignore"), ".env.local") + await fs.writeFile(path.join(sourceDir, ".env.local"), "LOCAL_VAR=value") + + const result = await service.copyWorktreeIncludeFiles(sourceDir, targetDir, undefined, false) + + expect(result).toContain(".env.local") + const copiedContent = await fs.readFile(path.join(targetDir, ".env.local"), "utf-8") + expect(copiedContent).toBe("LOCAL_VAR=value") + + // Verify it's NOT a hard link (different inode for regular copy) + const sourceStats = await fs.stat(path.join(sourceDir, ".env.local")) + const targetStats = await fs.stat(path.join(targetDir, ".env.local")) + expect(targetStats.ino).not.toBe(sourceStats.ino) + }) + + it("should report progress when hard-linking directories", async () => { + await fs.writeFile(path.join(sourceDir, ".worktreeinclude"), "node_modules") + await fs.writeFile(path.join(sourceDir, ".gitignore"), "node_modules") + await fs.mkdir(path.join(sourceDir, "node_modules"), { recursive: true }) + await fs.writeFile(path.join(sourceDir, "node_modules", "test.txt"), "test content") + + const progressCalls: Array<{ bytesCopied: number; itemName: string }> = [] + const onProgress = vi.fn((progress: { bytesCopied: number; itemName: string }) => { + progressCalls.push({ ...progress }) + }) + + await service.copyWorktreeIncludeFiles(sourceDir, targetDir, onProgress, true) + + expect(onProgress).toHaveBeenCalled() + expect(progressCalls.length).toBeGreaterThan(0) + const finalCall = progressCalls[progressCalls.length - 1] + expect(finalCall?.bytesCopied).toBeGreaterThan(0) + }) + }) }) }) diff --git a/packages/core/src/worktree/worktree-include.ts b/packages/core/src/worktree/worktree-include.ts index 09156eb28c..d40c76f60d 100644 --- a/packages/core/src/worktree/worktree-include.ts +++ b/packages/core/src/worktree/worktree-include.ts @@ -112,12 +112,14 @@ export class WorktreeIncludeService { * @param sourceDir - The source directory containing the files to copy * @param targetDir - The target directory where files will be copied * @param onProgress - Optional callback to report copy progress (size-based) + * @param useHardLinks - If true, use hard links instead of copies when possible (defaults to false) * @returns Array of copied file/directory paths */ async copyWorktreeIncludeFiles( sourceDir: string, targetDir: string, onProgress?: CopyProgressCallback, + useHardLinks?: boolean, ): Promise { const worktreeIncludePath = path.join(sourceDir, ".worktreeinclude") const gitignorePath = path.join(sourceDir, ".gitignore") @@ -180,21 +182,37 @@ export class WorktreeIncludeService { const stats = await fs.stat(sourcePath) if (stats.isDirectory()) { - // Copy directory with progress tracking - bytesCopied = await this.copyDirectoryWithProgress( - sourcePath, - targetPath, - item, - bytesCopied, - onProgress, - ) + if (useHardLinks) { + // Recursively hard-link directory contents + bytesCopied = await this.hardLinkDirectoryWithProgress( + sourcePath, + targetPath, + item, + bytesCopied, + onProgress, + ) + } else { + // Copy directory with progress tracking + bytesCopied = await this.copyDirectoryWithProgress( + sourcePath, + targetPath, + item, + bytesCopied, + onProgress, + ) + } } else { // Report progress before copying onProgress?.({ bytesCopied, itemName: item }) // Ensure parent directory exists await fs.mkdir(path.dirname(targetPath), { recursive: true }) - await fs.copyFile(sourcePath, targetPath) + + if (useHardLinks) { + await this.hardLinkWithFallback(sourcePath, targetPath) + } else { + await fs.copyFile(sourcePath, targetPath) + } // Update bytes copied bytesCopied += this.getSizeOnDisk(stats) @@ -372,6 +390,90 @@ export class WorktreeIncludeService { return bytesCopiedBefore + finalSize } + /** + * Create a hard link for a single file, falling back to copy if hard linking fails + * (e.g., cross-device link, unsupported filesystem, or permissions issue). + */ + private async hardLinkWithFallback(source: string, target: string): Promise { + try { + await fs.link(source, target) + } catch { + // Fallback to regular copy if hard link fails + await fs.copyFile(source, target) + } + } + + /** + * Recursively hard-link all files in a directory from source to target. + * Creates the directory structure with fs.mkdir and hard-links each file. + * Falls back to copyDirectoryWithProgress if the initial hard-link attempt fails. + * Returns the updated bytesCopied count. + */ + private async hardLinkDirectoryWithProgress( + source: string, + target: string, + itemName: string, + bytesCopiedBefore: number, + onProgress?: CopyProgressCallback, + ): Promise { + try { + const bytesCopied = await this.hardLinkDirectoryRecursive( + source, + target, + itemName, + bytesCopiedBefore, + onProgress, + ) + return bytesCopied + } catch { + // If recursive hard-linking fails entirely, fall back to native copy + return this.copyDirectoryWithProgress(source, target, itemName, bytesCopiedBefore, onProgress) + } + } + + /** + * Recursively walk a directory, creating directories and hard-linking files. + * Reports progress as files are linked. + */ + private async hardLinkDirectoryRecursive( + source: string, + target: string, + itemName: string, + bytesCopiedBefore: number, + onProgress?: CopyProgressCallback, + ): Promise { + await fs.mkdir(target, { recursive: true }) + + const entries = await fs.readdir(source, { withFileTypes: true }) + let bytesCopied = bytesCopiedBefore + + for (const entry of entries) { + const sourcePath = path.join(source, entry.name) + const targetPath = path.join(target, entry.name) + + if (entry.isDirectory()) { + bytesCopied = await this.hardLinkDirectoryRecursive( + sourcePath, + targetPath, + itemName, + bytesCopied, + onProgress, + ) + } else if (entry.isFile()) { + await this.hardLinkWithFallback(sourcePath, targetPath) + const stats = await fs.stat(sourcePath) + bytesCopied += this.getSizeOnDisk(stats) + + onProgress?.({ + bytesCopied, + itemName, + }) + } + } + + return bytesCopied + } + /** * Parse a .gitignore-style file and return the patterns */ diff --git a/packages/types/src/vscode-extension-host.ts b/packages/types/src/vscode-extension-host.ts index a4ef802efb..e8cde57f03 100644 --- a/packages/types/src/vscode-extension-host.ts +++ b/packages/types/src/vscode-extension-host.ts @@ -625,6 +625,7 @@ export interface WebviewMessage { worktreeCreateNewBranch?: boolean worktreeForce?: boolean worktreeNewWindow?: boolean + worktreeUseHardLinks?: boolean worktreeIncludeContent?: string } diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index fac7ed10d5..b262251369 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -3007,6 +3007,7 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We copyProgressItemName: progress.itemName, }) }, + message.worktreeUseHardLinks, ) await provider.postMessageToWebview({ type: "worktreeResult", success, text }) diff --git a/src/core/webview/worktree/handlers.ts b/src/core/webview/worktree/handlers.ts index 67c88b910e..aa6efc7b64 100644 --- a/src/core/webview/worktree/handlers.ts +++ b/src/core/webview/worktree/handlers.ts @@ -137,6 +137,7 @@ export async function handleCreateWorktree( createNewBranch?: boolean }, onCopyProgress?: CopyProgressCallback, + useHardLinks?: boolean, ): Promise { const cwd = provider.cwd @@ -158,6 +159,7 @@ export async function handleCreateWorktree( cwd, result.worktree.path, onCopyProgress, + useHardLinks, ) if (copiedItems.length > 0) { result.message += ` (copied ${copiedItems.length} item(s) from .worktreeinclude)` diff --git a/webview-ui/src/components/worktrees/CreateWorktreeModal.tsx b/webview-ui/src/components/worktrees/CreateWorktreeModal.tsx index 84743c1650..0e482d8b03 100644 --- a/webview-ui/src/components/worktrees/CreateWorktreeModal.tsx +++ b/webview-ui/src/components/worktrees/CreateWorktreeModal.tsx @@ -7,7 +7,7 @@ import { vscode } from "@/utils/vscode" import { useAppTranslation } from "@/i18n/TranslationContext" import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, Button, Input } from "@/components/ui" import { SearchableSelect, type SearchableSelectOption } from "@/components/ui/searchable-select" -import { CornerDownRight, Folder, FolderSearch, Info } from "lucide-react" +import { CornerDownRight, Folder, FolderSearch, Info, Link } from "lucide-react" interface CreateWorktreeModalProps { open: boolean @@ -34,6 +34,9 @@ export const CreateWorktreeModal = ({ const [branches, setBranches] = useState(null) const [includeStatus, setIncludeStatus] = useState(null) + // Hard links option + const [useHardLinks, setUseHardLinks] = useState(true) + // UI state const [isCreating, setIsCreating] = useState(false) const [error, setError] = useState(null) @@ -121,8 +124,9 @@ export const CreateWorktreeModal = ({ worktreeBranch: branchName, worktreeBaseBranch: baseBranch, worktreeCreateNewBranch: true, + worktreeUseHardLinks: useHardLinks, }) - }, [worktreePath, branchName, baseBranch]) + }, [worktreePath, branchName, baseBranch, useHardLinks]) const isValid = branchName.trim() && worktreePath.trim() && baseBranch.trim() @@ -215,6 +219,22 @@ export const CreateWorktreeModal = ({ /> + {/* Hard links option - only show when .worktreeinclude exists */} + {includeStatus?.exists && ( +
+ + +
+ )} + {/* Error message */} {error && (
diff --git a/webview-ui/src/i18n/locales/ca/worktrees.json b/webview-ui/src/i18n/locales/ca/worktrees.json index 525e1ca461..52ebc9441e 100644 --- a/webview-ui/src/i18n/locales/ca/worktrees.json +++ b/webview-ui/src/i18n/locales/ca/worktrees.json @@ -38,6 +38,8 @@ "creating": "S'està creant...", "copyingFiles": "S'estan copiant fitxers...", "copyingProgress": "{{item}} — {{copied}} copiat", + "useHardLinks": "Utilitza enllaços durs per als fitxers inclosos", + "useHardLinksTooltip": "Crea enllaços durs en lloc de còpies, estalviant espai de disc i accelerant la creació del worktree. Desactiva-ho si l'origen i el destí són en sistemes de fitxers diferents.", "cancel": "Cancel·la", "deleteWorktree": "Suprimeix worktree", "deleteWarning": "Aquesta acció no es pot desfer. S'eliminarà:", diff --git a/webview-ui/src/i18n/locales/de/worktrees.json b/webview-ui/src/i18n/locales/de/worktrees.json index 3b44a9eb38..1f569a4d45 100644 --- a/webview-ui/src/i18n/locales/de/worktrees.json +++ b/webview-ui/src/i18n/locales/de/worktrees.json @@ -38,6 +38,8 @@ "creating": "Wird erstellt...", "copyingFiles": "Dateien werden kopiert...", "copyingProgress": "{{item}} — {{copied}} kopiert", + "useHardLinks": "Hardlinks für eingeschlossene Dateien verwenden", + "useHardLinksTooltip": "Erstellt Hardlinks statt Kopien, spart Speicherplatz und beschleunigt die Worktree-Erstellung. Deaktiviere dies, wenn Quelle und Ziel auf verschiedenen Dateisystemen liegen.", "cancel": "Abbrechen", "deleteWorktree": "Worktree löschen", "deleteWarning": "Diese Aktion kann nicht rückgängig gemacht werden. Folgendes wird gelöscht:", diff --git a/webview-ui/src/i18n/locales/en/worktrees.json b/webview-ui/src/i18n/locales/en/worktrees.json index 17b5ae75f8..ea0e3c44e1 100644 --- a/webview-ui/src/i18n/locales/en/worktrees.json +++ b/webview-ui/src/i18n/locales/en/worktrees.json @@ -38,6 +38,8 @@ "creating": "Creating...", "copyingFiles": "Copying files...", "copyingProgress": "{{item}} — {{copied}} copied", + "useHardLinks": "Use hard links for included files", + "useHardLinksTooltip": "Creates hard links instead of copies, saving disk space and speeding up worktree creation. Disable if source and target are on different filesystems.", "cancel": "Cancel", "deleteWorktree": "Delete Worktree", "deleteWarning": "This can't be undone. It will delete:", diff --git a/webview-ui/src/i18n/locales/es/worktrees.json b/webview-ui/src/i18n/locales/es/worktrees.json index d7a72fe936..44f5c5b0a4 100644 --- a/webview-ui/src/i18n/locales/es/worktrees.json +++ b/webview-ui/src/i18n/locales/es/worktrees.json @@ -38,6 +38,8 @@ "creating": "Creando...", "copyingFiles": "Copiando archivos...", "copyingProgress": "{{item}} — {{copied}} copiado", + "useHardLinks": "Usar enlaces duros para archivos incluidos", + "useHardLinksTooltip": "Crea enlaces duros en lugar de copias, ahorrando espacio en disco y acelerando la creación del worktree. Desactívalo si el origen y el destino están en sistemas de archivos diferentes.", "cancel": "Cancelar", "deleteWorktree": "Eliminar Worktree", "deleteWarning": "Esta acción no se puede deshacer. Se eliminará lo siguiente:", diff --git a/webview-ui/src/i18n/locales/fr/worktrees.json b/webview-ui/src/i18n/locales/fr/worktrees.json index 4ae218e1bb..e5abc0c609 100644 --- a/webview-ui/src/i18n/locales/fr/worktrees.json +++ b/webview-ui/src/i18n/locales/fr/worktrees.json @@ -38,6 +38,8 @@ "creating": "Création...", "copyingFiles": "Copie des fichiers...", "copyingProgress": "{{item}} — {{copied}} copié", + "useHardLinks": "Utiliser des liens physiques pour les fichiers inclus", + "useHardLinksTooltip": "Crée des liens physiques au lieu de copies, économisant de l'espace disque et accélérant la création du worktree. Désactive cette option si la source et la cible sont sur des systèmes de fichiers différents.", "cancel": "Annuler", "deleteWorktree": "Supprimer le worktree", "deleteWarning": "Cette action est irréversible. Les éléments suivants seront supprimés :", diff --git a/webview-ui/src/i18n/locales/hi/worktrees.json b/webview-ui/src/i18n/locales/hi/worktrees.json index 214abb3d04..24667c8e16 100644 --- a/webview-ui/src/i18n/locales/hi/worktrees.json +++ b/webview-ui/src/i18n/locales/hi/worktrees.json @@ -38,6 +38,8 @@ "creating": "बनाया जा रहा है...", "copyingFiles": "फ़ाइलें कॉपी की जा रही हैं...", "copyingProgress": "{{item}} — {{copied}} कॉपी किया गया", + "useHardLinks": "शामिल फ़ाइलों के लिए हार्ड लिंक का उपयोग करें", + "useHardLinksTooltip": "कॉपी के बजाय हार्ड लिंक बनाता है, डिस्क स्थान बचाता है और worktree निर्माण को तेज़ करता है। यदि स्रोत और लक्ष्य अलग-अलग फ़ाइल सिस्टम पर हैं तो इसे अक्षम करें।", "cancel": "रद्द करें", "deleteWorktree": "Worktree हटाएँ", "deleteWarning": "यह कार्रवाई वापस नहीं ली जा सकती। निम्नलिखित हटाया जाएगा:", diff --git a/webview-ui/src/i18n/locales/id/worktrees.json b/webview-ui/src/i18n/locales/id/worktrees.json index a21466c755..98cd97a5b3 100644 --- a/webview-ui/src/i18n/locales/id/worktrees.json +++ b/webview-ui/src/i18n/locales/id/worktrees.json @@ -38,6 +38,8 @@ "creating": "Membuat...", "copyingFiles": "Menyalin file...", "copyingProgress": "{{item}} — {{copied}} disalin", + "useHardLinks": "Gunakan hard link untuk file yang disertakan", + "useHardLinksTooltip": "Membuat hard link alih-alih salinan, menghemat ruang disk dan mempercepat pembuatan worktree. Nonaktifkan jika sumber dan target berada di sistem file yang berbeda.", "cancel": "Batal", "deleteWorktree": "Hapus Worktree", "deleteWarning": "Tindakan ini tidak dapat dibatalkan. Yang berikut akan dihapus:", diff --git a/webview-ui/src/i18n/locales/it/worktrees.json b/webview-ui/src/i18n/locales/it/worktrees.json index d929d6df15..684b8c8834 100644 --- a/webview-ui/src/i18n/locales/it/worktrees.json +++ b/webview-ui/src/i18n/locales/it/worktrees.json @@ -38,6 +38,8 @@ "creating": "Creazione...", "copyingFiles": "Copia dei file...", "copyingProgress": "{{item}} — {{copied}} copiato", + "useHardLinks": "Usa hard link per i file inclusi", + "useHardLinksTooltip": "Crea hard link invece di copie, risparmiando spazio su disco e velocizzando la creazione del worktree. Disattiva se origine e destinazione sono su filesystem diversi.", "cancel": "Annulla", "deleteWorktree": "Elimina Worktree", "deleteWarning": "Questa azione non può essere annullata. Verrà eliminato quanto segue:", diff --git a/webview-ui/src/i18n/locales/ja/worktrees.json b/webview-ui/src/i18n/locales/ja/worktrees.json index 9e89aa9c92..b62e2b4667 100644 --- a/webview-ui/src/i18n/locales/ja/worktrees.json +++ b/webview-ui/src/i18n/locales/ja/worktrees.json @@ -38,6 +38,8 @@ "creating": "作成中...", "copyingFiles": "ファイルをコピー中...", "copyingProgress": "{{item}} — {{copied}} コピー済み", + "useHardLinks": "インクルードファイルにハードリンクを使用する", + "useHardLinksTooltip": "コピーの代わりにハードリンクを作成し、ディスク容量を節約してworktreeの作成を高速化します。ソースとターゲットが異なるファイルシステムにある場合は無効にしてください。", "cancel": "キャンセル", "deleteWorktree": "Worktree を削除", "deleteWarning": "この操作は元に戻せません。次のものが削除されます:", diff --git a/webview-ui/src/i18n/locales/ko/worktrees.json b/webview-ui/src/i18n/locales/ko/worktrees.json index 4573e2382b..352bbf4412 100644 --- a/webview-ui/src/i18n/locales/ko/worktrees.json +++ b/webview-ui/src/i18n/locales/ko/worktrees.json @@ -38,6 +38,8 @@ "creating": "만드는 중...", "copyingFiles": "파일 복사 중...", "copyingProgress": "{{item}} — {{copied}} 복사됨", + "useHardLinks": "포함된 파일에 하드 링크 사용", + "useHardLinksTooltip": "복사 대신 하드 링크를 생성하여 디스크 공간을 절약하고 worktree 생성 속도를 높입니다. 소스와 대상이 다른 파일 시스템에 있는 경우 비활성화하세요.", "cancel": "취소", "deleteWorktree": "Worktree 삭제", "deleteWarning": "이 작업은 되돌릴 수 없습니다. 다음이 삭제됩니다:", diff --git a/webview-ui/src/i18n/locales/nl/worktrees.json b/webview-ui/src/i18n/locales/nl/worktrees.json index cb482fa498..650e1c07d3 100644 --- a/webview-ui/src/i18n/locales/nl/worktrees.json +++ b/webview-ui/src/i18n/locales/nl/worktrees.json @@ -38,6 +38,8 @@ "creating": "Bezig met aanmaken...", "copyingFiles": "Bestanden kopiëren...", "copyingProgress": "{{item}} — {{copied}} gekopieerd", + "useHardLinks": "Gebruik harde links voor meegeleverde bestanden", + "useHardLinksTooltip": "Maakt harde links aan in plaats van kopieën, bespaart schijfruimte en versnelt het aanmaken van de worktree. Schakel uit als bron en doel op verschillende bestandssystemen staan.", "cancel": "Annuleren", "deleteWorktree": "Worktree verwijderen", "deleteWarning": "Deze actie kan niet ongedaan worden gemaakt. Het volgende wordt verwijderd:", diff --git a/webview-ui/src/i18n/locales/pl/worktrees.json b/webview-ui/src/i18n/locales/pl/worktrees.json index c1206eccf3..3b491e7ca3 100644 --- a/webview-ui/src/i18n/locales/pl/worktrees.json +++ b/webview-ui/src/i18n/locales/pl/worktrees.json @@ -38,6 +38,8 @@ "creating": "Tworzenie...", "copyingFiles": "Kopiowanie plików...", "copyingProgress": "{{item}} — {{copied}} skopiowano", + "useHardLinks": "Użyj twardych dowiązań dla dołączonych plików", + "useHardLinksTooltip": "Tworzy twarde dowiązania zamiast kopii, oszczędzając miejsce na dysku i przyspieszając tworzenie worktree. Wyłącz, jeśli źródło i cel znajdują się na różnych systemach plików.", "cancel": "Anuluj", "deleteWorktree": "Usuń Worktree", "deleteWarning": "Tej akcji nie można cofnąć. Zostanie usunięte:", diff --git a/webview-ui/src/i18n/locales/pt-BR/worktrees.json b/webview-ui/src/i18n/locales/pt-BR/worktrees.json index 1bf55fb2d4..0165aa140a 100644 --- a/webview-ui/src/i18n/locales/pt-BR/worktrees.json +++ b/webview-ui/src/i18n/locales/pt-BR/worktrees.json @@ -38,6 +38,8 @@ "creating": "Criando...", "copyingFiles": "Copiando arquivos...", "copyingProgress": "{{item}} — {{copied}} copiado", + "useHardLinks": "Usar hard links para arquivos incluídos", + "useHardLinksTooltip": "Cria hard links em vez de cópias, economizando espaço em disco e acelerando a criação do worktree. Desative se a origem e o destino estiverem em sistemas de arquivos diferentes.", "cancel": "Cancelar", "deleteWorktree": "Excluir Worktree", "deleteWarning": "Esta ação não pode ser desfeita. O seguinte será excluído:", diff --git a/webview-ui/src/i18n/locales/ru/worktrees.json b/webview-ui/src/i18n/locales/ru/worktrees.json index a4c69e95ae..c817f08ef2 100644 --- a/webview-ui/src/i18n/locales/ru/worktrees.json +++ b/webview-ui/src/i18n/locales/ru/worktrees.json @@ -38,6 +38,8 @@ "creating": "Создание...", "copyingFiles": "Копирование файлов...", "copyingProgress": "{{item}} — {{copied}} скопировано", + "useHardLinks": "Использовать жёсткие ссылки для включённых файлов", + "useHardLinksTooltip": "Создаёт жёсткие ссылки вместо копий, экономя место на диске и ускоряя создание worktree. Отключи, если источник и цель находятся на разных файловых системах.", "cancel": "Отмена", "deleteWorktree": "Удалить Worktree", "deleteWarning": "Это действие нельзя отменить. Будет удалено:", diff --git a/webview-ui/src/i18n/locales/tr/worktrees.json b/webview-ui/src/i18n/locales/tr/worktrees.json index f270600b44..de92b72323 100644 --- a/webview-ui/src/i18n/locales/tr/worktrees.json +++ b/webview-ui/src/i18n/locales/tr/worktrees.json @@ -38,6 +38,8 @@ "creating": "Oluşturuluyor...", "copyingFiles": "Dosyalar kopyalanıyor...", "copyingProgress": "{{item}} — {{copied}} kopyalandı", + "useHardLinks": "Dahil edilen dosyalar için sabit bağlantı kullan", + "useHardLinksTooltip": "Kopya yerine sabit bağlantılar oluşturur, disk alanından tasarruf sağlar ve worktree oluşturmayı hızlandırır. Kaynak ve hedef farklı dosya sistemlerindeyse devre dışı bırak.", "cancel": "İptal", "deleteWorktree": "Worktree'yi sil", "deleteWarning": "Bu işlem geri alınamaz. Şunlar silinecek:", diff --git a/webview-ui/src/i18n/locales/vi/worktrees.json b/webview-ui/src/i18n/locales/vi/worktrees.json index 0efe80a05a..34f1ff89bc 100644 --- a/webview-ui/src/i18n/locales/vi/worktrees.json +++ b/webview-ui/src/i18n/locales/vi/worktrees.json @@ -38,6 +38,8 @@ "creating": "Đang tạo...", "copyingFiles": "Đang sao chép tệp...", "copyingProgress": "{{item}} — {{copied}} đã sao chép", + "useHardLinks": "Sử dụng liên kết cứng cho các tệp được bao gồm", + "useHardLinksTooltip": "Tạo liên kết cứng thay vì bản sao, tiết kiệm dung lượng đĩa và tăng tốc tạo worktree. Tắt nếu nguồn và đích nằm trên các hệ thống tệp khác nhau.", "cancel": "Hủy", "deleteWorktree": "Xóa Worktree", "deleteWarning": "Không thể hoàn tác thao tác này. Những thứ sau sẽ bị xóa:", diff --git a/webview-ui/src/i18n/locales/zh-CN/worktrees.json b/webview-ui/src/i18n/locales/zh-CN/worktrees.json index 04b0b8384a..9f987bb7e4 100644 --- a/webview-ui/src/i18n/locales/zh-CN/worktrees.json +++ b/webview-ui/src/i18n/locales/zh-CN/worktrees.json @@ -38,6 +38,8 @@ "creating": "正在创建...", "copyingFiles": "正在复制文件...", "copyingProgress": "{{item}} — {{copied}} 已复制", + "useHardLinks": "为包含的文件使用硬链接", + "useHardLinksTooltip": "创建硬链接而非副本,节省磁盘空间并加快 worktree 创建速度。如果源和目标位于不同的文件系统上,请禁用此选项。", "cancel": "取消", "deleteWorktree": "删除 Worktree", "deleteWarning": "此操作不可逆。将删除以下内容:", diff --git a/webview-ui/src/i18n/locales/zh-TW/worktrees.json b/webview-ui/src/i18n/locales/zh-TW/worktrees.json index 2bf2499b52..204944b06a 100644 --- a/webview-ui/src/i18n/locales/zh-TW/worktrees.json +++ b/webview-ui/src/i18n/locales/zh-TW/worktrees.json @@ -38,6 +38,8 @@ "creating": "正在建立...", "copyingFiles": "正在複製檔案...", "copyingProgress": "{{item}} — {{copied}} 已複製", + "useHardLinks": "為包含的檔案使用硬連結", + "useHardLinksTooltip": "建立硬連結而非副本,節省磁碟空間並加快 worktree 建立速度。如果來源和目標位於不同的檔案系統上,請停用此選項。", "cancel": "取消", "deleteWorktree": "刪除 Worktree", "deleteWarning": "此操作無法復原。將刪除以下內容:",