Skip to content
Open
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
15 changes: 13 additions & 2 deletions extensions/git/src/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1473,14 +1473,25 @@ export class Repository implements Disposable {
if (toClean.length > 0) {
if (discardUntrackedChangesToTrash) {
try {
// Helper function to get the real path for subst drives
const getRealPath = (fsPath: string) => {
return this.rootRealPath && fsPath.startsWith(this.root)
? path.join(this.rootRealPath, fsPath.substring(this.root.length))
: fsPath;
};

// Attempt to move the first resource to the recycle bin/trash to check
// if it is supported. If it fails, we show a confirmation dialog and
// fall back to deletion.
await workspace.fs.delete(Uri.file(toClean[0]), { useTrash: true });
const firstFileRealPath = getRealPath(toClean[0]);
await workspace.fs.delete(Uri.file(firstFileRealPath), { useTrash: true });

const limiter = new Limiter<void>(5);
await Promise.all(toClean.slice(1).map(fsPath => limiter.queue(
async () => await workspace.fs.delete(Uri.file(fsPath), { useTrash: true }))));
async () => {
const realPath = getRealPath(fsPath);
await workspace.fs.delete(Uri.file(realPath), { useTrash: true });
})));
} catch {
const message = isWindows
? l10n.t('Failed to delete using the Recycle Bin. Do you want to permanently delete instead?')
Expand Down
61 changes: 61 additions & 0 deletions extensions/git/src/test/repository.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import 'mocha';
import assert from 'assert';
import path from 'path';
import { isWindows } from '../util';

suite('Repository path handling', () => {
// Skip all tests if not on Windows, as these tests are for Windows-specific path handling
suiteSetup(function () {
if (!isWindows) {
this.skip();
}
});

// Test the path manipulation logic directly for subst drives
test('subst drive path correction', () => {
const root = 'X:\\repo';
const rootRealPath = 'C:\\real\\path\\repo';
const fsPath = 'X:\\repo\\file.txt';

// This is the logic from the fix we implemented
const realPath = fsPath.startsWith(root)
? path.join(rootRealPath, fsPath.substring(root.length))
: fsPath;

// Verify the path is correctly transformed
assert.strictEqual(realPath, 'C:\\real\\path\\repo\\file.txt');
});

test('non-subst path should remain unchanged', () => {
const root = 'C:\\repo';
const rootRealPath = 'C:\\repo';
const fsPath = 'C:\\repo\\file.txt';

// Same logic as implemented in the fix
const realPath = fsPath.startsWith(root)
? path.join(rootRealPath, fsPath.substring(root.length))
: fsPath;

// Path should remain unchanged
assert.strictEqual(realPath, 'C:\\repo\\file.txt');
});

test('path outside repository should remain unchanged', () => {
const root = 'X:\\repo';
const rootRealPath = 'C:\\real\\path\\repo';
const fsPath = 'D:\\other\\file.txt';

// Same logic as implemented in the fix
const realPath = fsPath.startsWith(root)
? path.join(rootRealPath, fsPath.substring(root.length))
: fsPath;

// Path should remain unchanged
assert.strictEqual(realPath, 'D:\\other\\file.txt');
});
});