From 3233fc4812afc974aa3624e1f6177ee9bb4e0d09 Mon Sep 17 00:00:00 2001 From: Kazuki Yamada Date: Fri, 10 Oct 2025 23:21:16 +0900 Subject: [PATCH] fix(cli): Skip file copy when source and target paths are identical When using the remote repository feature with an absolute path specified for the output file, the previous implementation attempted to copy the file even though source and target resolved to the same path. This resulted in an error when trying to copy a file to itself. This commit fixes the issue by: - Checking if source and target paths are identical before copying - Skipping the copy operation when they are the same - This handles absolute paths and any other edge cases where paths resolve to the same location The fix is implemented in copyOutputToCurrentDirectory function itself, making it more robust and self-contained. Fixes #873 --- src/cli/actions/remoteAction.ts | 7 ++++++ tests/cli/actions/remoteAction.test.ts | 30 +++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/cli/actions/remoteAction.ts b/src/cli/actions/remoteAction.ts index a1b8431a5..d081fdb83 100644 --- a/src/cli/actions/remoteAction.ts +++ b/src/cli/actions/remoteAction.ts @@ -191,6 +191,13 @@ export const copyOutputToCurrentDirectory = async ( const sourcePath = path.resolve(sourceDir, outputFileName); const targetPath = path.resolve(targetDir, outputFileName); + // Skip copy if source and target are the same + // This can happen when an absolute path is specified for the output file + if (sourcePath === targetPath) { + logger.trace(`Source and target are the same (${sourcePath}), skipping copy`); + return; + } + try { logger.trace(`Copying output file from: ${sourcePath} to: ${targetPath}`); diff --git a/tests/cli/actions/remoteAction.test.ts b/tests/cli/actions/remoteAction.test.ts index 9765b12fc..04967c62e 100644 --- a/tests/cli/actions/remoteAction.test.ts +++ b/tests/cli/actions/remoteAction.test.ts @@ -205,7 +205,7 @@ describe('remoteAction functions', () => { }); describe('copyOutputToCurrentDirectory', () => { - test('should copy output file', async () => { + test('should copy output file when source and target are different', async () => { const sourceDir = '/source/dir'; const targetDir = '/target/dir'; const fileName = 'output.txt'; @@ -217,6 +217,34 @@ describe('remoteAction functions', () => { expect(fs.copyFile).toHaveBeenCalledWith(path.resolve(sourceDir, fileName), path.resolve(targetDir, fileName)); }); + test('should skip copy when source and target are the same', async () => { + const sourceDir = '/tmp/dir'; + const targetDir = '/tmp/dir'; + const fileName = 'output.txt'; + + vi.mocked(fs.copyFile).mockResolvedValue(); + + await copyOutputToCurrentDirectory(sourceDir, targetDir, fileName); + + // Should not call copyFile when source and target are the same + expect(fs.copyFile).not.toHaveBeenCalled(); + }); + + test('should skip copy when absolute path resolves to same location', async () => { + const sourceDir = '/tmp/repomix-123'; + const targetDir = process.cwd(); + const absolutePath = '/tmp/my_private_dir/output.xml'; + + vi.mocked(fs.copyFile).mockResolvedValue(); + + await copyOutputToCurrentDirectory(sourceDir, targetDir, absolutePath); + + // When absolute path is used, both source and target resolve to the same path + // path.resolve('/tmp/repomix-123', '/tmp/my_private_dir/output.xml') -> '/tmp/my_private_dir/output.xml' + // path.resolve(process.cwd(), '/tmp/my_private_dir/output.xml') -> '/tmp/my_private_dir/output.xml' + expect(fs.copyFile).not.toHaveBeenCalled(); + }); + test('should throw error when copy fails', async () => { const sourceDir = '/source/dir'; const targetDir = '/target/dir';