Skip to content

Commit 8a9a745

Browse files
committed
Fixes gitkraken#708 - Prompt for file path when missing from revision
Improves the `openFileAtRevision()` function by: - Asking user to enter another path when the requested file doesn't exist in the selected revision. - Showing an error message on subsequent failure (or if an error is thrown). The above function is used by a number of commands: - `gitlens.openFileRevision` - `gitlens.openFileRevisionFrom` - `gitlens.openRevisionFile`
1 parent e6c9fff commit 8a9a745

File tree

3 files changed

+89
-7
lines changed

3 files changed

+89
-7
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
66

77
## [Unreleased]
88

9+
### Added
10+
11+
- Adds support for opening renamed/deleted files using the _Open File at Revision..._ commands by showing a quickpick prompt if the requested file doesn't exist in the selected revision — thanks to [PR #2825](https://github.com/gitkraken/vscode-gitlens/pull/2825) by Victor Hallberg ([@mogelbrod](https://github.com/mogelbrod))
12+
913
### Fixed
1014

1115
- Fixes [#2482](https://github.com/gitkraken/vscode-gitlens/issues/2482) - Unresponsive "commits" view and "branches" view update due to git log

src/git/actions/commit.ts

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { TextDocumentShowOptions } from 'vscode';
1+
import type { TextDocumentShowOptions, TextEditor } from 'vscode';
22
import { env, Range, Uri, window, workspace } from 'vscode';
33
import type { DiffWithCommandArgs } from '../../commands/diffWith';
44
import type { DiffWithPreviousCommandArgs } from '../../commands/diffWithPrevious';
@@ -12,6 +12,7 @@ import type { FileAnnotationType } from '../../config';
1212
import { Commands } from '../../constants';
1313
import { Container } from '../../container';
1414
import type { ShowInCommitGraphCommandArgs } from '../../plus/webviews/graph/protocol';
15+
import { showRevisionPicker } from '../../quickpicks/revisionPicker';
1516
import { executeCommand, executeEditorCommand } from '../../system/command';
1617
import { findOrOpenEditor, findOrOpenEditors } from '../../system/utils';
1718
import { GitUri } from '../gitUri';
@@ -402,7 +403,7 @@ export async function openFileAtRevision(
402403
commitOrOptions?: GitCommit | TextDocumentShowOptions,
403404
options?: TextDocumentShowOptions & { annotationType?: FileAnnotationType; line?: number },
404405
): Promise<void> {
405-
let uri;
406+
let uri: Uri;
406407
if (fileOrRevisionUri instanceof Uri) {
407408
if (isCommit(commitOrOptions)) throw new Error('Invalid arguments');
408409

@@ -440,11 +441,30 @@ export async function openFileAtRevision(
440441
opts.selection = new Range(line, 0, line, 0);
441442
}
442443

443-
const editor = await findOrOpenEditor(uri, opts);
444-
if (annotationType != null && editor != null) {
445-
void (await Container.instance.fileAnnotations.show(editor, annotationType, {
446-
selection: { line: line },
447-
}));
444+
const gitUri = await GitUri.fromUri(uri);
445+
446+
let editor: TextEditor | undefined;
447+
try {
448+
editor = await findOrOpenEditor(uri, { throwOnError: true, ...opts }).catch(error => {
449+
if (error?.message?.includes('Unable to resolve nonexistent file')) {
450+
return showRevisionPicker(gitUri, {
451+
title: 'File not found in revision - pick another file to open instead',
452+
}).then(pickedUri => {
453+
return pickedUri ? findOrOpenEditor(pickedUri, opts) : undefined;
454+
});
455+
}
456+
throw error;
457+
});
458+
459+
if (annotationType != null && editor != null) {
460+
void (await Container.instance.fileAnnotations.show(editor, annotationType, {
461+
selection: { line: line },
462+
}));
463+
}
464+
} catch (error) {
465+
await window.showErrorMessage(
466+
`Unable to open '${gitUri.relativePath}' - file doesn't exist in selected revision`,
467+
);
448468
}
449469
}
450470

src/quickpicks/revisionPicker.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// import path from "path";
2+
import type { Disposable, Uri } from "vscode";
3+
import { window } from "vscode";
4+
import { Container } from "../container";
5+
import type { GitUri } from "../git/gitUri";
6+
import { filterMap } from "../system/iterable";
7+
import { getQuickPickIgnoreFocusOut } from "../system/utils";
8+
9+
export async function showRevisionPicker(
10+
uri: GitUri,
11+
options: {
12+
title: string;
13+
initialPath?: string;
14+
},
15+
): Promise<Uri | undefined> {
16+
const disposables: Disposable[] = [];
17+
try {
18+
const picker = window.createQuickPick();
19+
picker.title = options.title;
20+
picker.value = options.initialPath ?? uri.relativePath;
21+
picker.placeholder = 'Enter path to file...';
22+
picker.matchOnDescription = true;
23+
picker.busy = true;
24+
picker.ignoreFocusOut = getQuickPickIgnoreFocusOut();
25+
26+
picker.show();
27+
28+
const tree = await Container.instance.git.getTreeForRevision(uri.repoPath, uri.sha!);
29+
picker.items = Array.from(filterMap(tree, file => {
30+
// Exclude directories
31+
if (file.type !== 'blob') { return null }
32+
return { label: file.path }
33+
// FIXME: Remove this unless we opt to show the directory in the description
34+
// const parsed = path.parse(file.path)
35+
// return { label: parsed.base, description: parsed.dir }
36+
}))
37+
picker.busy = false;
38+
39+
const pick = await new Promise<string | undefined>(resolve => {
40+
disposables.push(
41+
picker,
42+
picker.onDidHide(() => resolve(undefined)),
43+
picker.onDidAccept(() => {
44+
if (picker.activeItems.length === 0) return;
45+
resolve(picker.activeItems[0].label);
46+
}),
47+
);
48+
});
49+
50+
return pick
51+
? Container.instance.git.getRevisionUri(uri.sha!, `${uri.repoPath}/${pick}`, uri.repoPath!)
52+
: undefined;
53+
} finally {
54+
disposables.forEach(d => {
55+
d.dispose();
56+
});
57+
}
58+
}

0 commit comments

Comments
 (0)