Browse Source

Fixes issues with missing worktrees

main
Eric Amodio 1 year ago
parent
commit
acc97f7084
8 changed files with 95 additions and 27 deletions
  1. +1
    -0
      CHANGELOG.md
  2. +10
    -1
      package.json
  3. +5
    -1
      src/commands/git/worktree.ts
  4. +16
    -5
      src/commands/quickCommand.steps.ts
  5. +2
    -1
      src/constants.ts
  6. +6
    -1
      src/quickpicks/items/gitCommands.ts
  7. +30
    -3
      src/views/nodes/worktreeNode.ts
  8. +25
    -15
      src/views/worktreesView.ts

+ 1
- 0
CHANGELOG.md View File

@ -24,6 +24,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
### Fixed ### Fixed
- Fixes [#2823](https://github.com/gitkraken/vscode-gitlens/issues/2823) - Handle stdout/stderr Buffers in shell run() — thanks to [PR #2824](https://github.com/gitkraken/vscode-gitlens/pull/2824) by Victor Hallberg ([@mogelbrod](https://github.com/mogelbrod)) - Fixes [#2823](https://github.com/gitkraken/vscode-gitlens/issues/2823) - Handle stdout/stderr Buffers in shell run() — thanks to [PR #2824](https://github.com/gitkraken/vscode-gitlens/pull/2824) by Victor Hallberg ([@mogelbrod](https://github.com/mogelbrod))
- Fixes issues with missing worktrees breaking the Worktrees view and quick pick menus
## [14.1.1] - 2023-07-18 ## [14.1.1] - 2023-07-18

+ 10
- 1
package.json View File

@ -4268,7 +4268,7 @@
} }
}, },
{ {
"id": "gitlens.decorations.worktreeView.hasUncommittedChangesForegroundColor",
"id": "gitlens.decorations.worktreeHasUncommittedChangesForegroundColor",
"description": "Specifies the decoration foreground color for worktrees that have uncommitted changes", "description": "Specifies the decoration foreground color for worktrees that have uncommitted changes",
"defaults": { "defaults": {
"light": "#895503", "light": "#895503",
@ -4277,6 +4277,15 @@
} }
}, },
{ {
"id": "gitlens.decorations.worktreeMissingForegroundColor",
"description": "Specifies the decoration foreground color for worktrees cannot be found on disk",
"defaults": {
"light": "#ad0707",
"dark": "#c74e39",
"highContrast": "#c74e39"
}
},
{
"id": "gitlens.graphLane1Color", "id": "gitlens.graphLane1Color",
"description": "Specifies the color for the first commit lane of the _Commit Graph_ visualization", "description": "Specifies the color for the first commit lane of the _Commit Graph_ visualization",
"defaults": { "defaults": {

+ 5
- 1
src/commands/git/worktree.ts View File

@ -723,7 +723,11 @@ export class WorktreeGitCommand extends QuickCommand {
try { try {
if (force) { if (force) {
const worktree = context.worktrees.find(wt => wt.uri.toString() === uri.toString()); const worktree = context.worktrees.find(wt => wt.uri.toString() === uri.toString());
const status = await worktree?.getStatus();
let status;
try {
status = await worktree?.getStatus();
} catch {}
if (status?.hasChanges ?? false) { if (status?.hasChanges ?? false) {
const confirm: MessageItem = { title: 'Force Delete' }; const confirm: MessageItem = { title: 'Force Delete' };
const cancel: MessageItem = { title: 'Cancel', isCloseAffordance: true }; const cancel: MessageItem = { title: 'Cancel', isCloseAffordance: true };

+ 16
- 5
src/commands/quickCommand.steps.ts View File

@ -229,18 +229,29 @@ export async function getWorktrees(
return Promise.all<WorktreeQuickPickItem>([ return Promise.all<WorktreeQuickPickItem>([
...worktrees ...worktrees
.filter(w => filter == null || filter(w)) .filter(w => filter == null || filter(w))
.map(async w =>
createWorktreeQuickPickItem(
.map(async w => {
let missing = false;
let status;
if (includeStatus) {
try {
status = await w.getStatus();
} catch {
missing = true;
}
}
return createWorktreeQuickPickItem(
w, w,
picked != null && picked != null &&
(typeof picked === 'string' ? w.uri.toString() === picked : picked.includes(w.uri.toString())), (typeof picked === 'string' ? w.uri.toString() === picked : picked.includes(w.uri.toString())),
missing,
{ {
buttons: buttons, buttons: buttons,
path: true, path: true,
status: includeStatus ? await w.getStatus() : undefined,
status: status,
}, },
),
),
);
}),
]); ]);
} }

+ 2
- 1
src/constants.ts View File

@ -77,7 +77,8 @@ export type Colors =
| `${typeof extensionPrefix}.decorations.workspaceCurrentForegroundColor` | `${typeof extensionPrefix}.decorations.workspaceCurrentForegroundColor`
| `${typeof extensionPrefix}.decorations.workspaceRepoMissingForegroundColor` | `${typeof extensionPrefix}.decorations.workspaceRepoMissingForegroundColor`
| `${typeof extensionPrefix}.decorations.workspaceRepoOpenForegroundColor` | `${typeof extensionPrefix}.decorations.workspaceRepoOpenForegroundColor`
| `${typeof extensionPrefix}.decorations.worktreeView.hasUncommittedChangesForegroundColor`
| `${typeof extensionPrefix}.decorations.worktreeHasUncommittedChangesForegroundColor`
| `${typeof extensionPrefix}.decorations.worktreeMissingForegroundColor`
| `${typeof extensionPrefix}.gutterBackgroundColor` | `${typeof extensionPrefix}.gutterBackgroundColor`
| `${typeof extensionPrefix}.gutterForegroundColor` | `${typeof extensionPrefix}.gutterForegroundColor`
| `${typeof extensionPrefix}.gutterUncommittedForegroundColor` | `${typeof extensionPrefix}.gutterUncommittedForegroundColor`

+ 6
- 1
src/quickpicks/items/gitCommands.ts View File

@ -469,6 +469,7 @@ export interface WorktreeQuickPickItem extends QuickPickItemOfT {
export function createWorktreeQuickPickItem( export function createWorktreeQuickPickItem(
worktree: GitWorktree, worktree: GitWorktree,
picked?: boolean, picked?: boolean,
missing?: boolean,
options?: { options?: {
alwaysShow?: boolean; alwaysShow?: boolean;
buttons?: QuickInputButton[]; buttons?: QuickInputButton[];
@ -510,7 +511,11 @@ export function createWorktreeQuickPickItem(
const item: WorktreeQuickPickItem = { const item: WorktreeQuickPickItem = {
label: `${icon}${GlyphChars.Space}${label}${options?.checked ? pad('$(check)', 2) : ''}`, label: `${icon}${GlyphChars.Space}${label}${options?.checked ? pad('$(check)', 2) : ''}`,
description: description, description: description,
detail: options?.path ? `In $(folder) ${worktree.friendlyPath}` : undefined,
detail: options?.path
? missing
? `${GlyphChars.Warning} Unable to locate $(folder) ${worktree.friendlyPath}`
: `In $(folder) ${worktree.friendlyPath}`
: undefined,
alwaysShow: options?.alwaysShow, alwaysShow: options?.alwaysShow,
buttons: options?.buttons, buttons: options?.buttons,
picked: picked, picked: picked,

+ 30
- 3
src/views/nodes/worktreeNode.ts View File

@ -12,6 +12,7 @@ import { getContext } from '../../system/context';
import { gate } from '../../system/decorators/gate'; import { gate } from '../../system/decorators/gate';
import { debug } from '../../system/decorators/log'; import { debug } from '../../system/decorators/log';
import { map } from '../../system/iterable'; import { map } from '../../system/iterable';
import { Logger } from '../../system/logger';
import type { Deferred } from '../../system/promise'; import type { Deferred } from '../../system/promise';
import { defer, getSettledValue } from '../../system/promise'; import { defer, getSettledValue } from '../../system/promise';
import { pad } from '../../system/string'; import { pad } from '../../system/string';
@ -207,6 +208,8 @@ export class WorktreeNode extends ViewNode {
} ` } `
: ''; : '';
let missing = false;
switch (this.worktree.type) { switch (this.worktree.type) {
case 'bare': case 'bare':
icon = new ThemeIcon('folder'); icon = new ThemeIcon('folder');
@ -217,7 +220,13 @@ export class WorktreeNode extends ViewNode {
); );
break; break;
case 'branch': { case 'branch': {
const [branch, status] = await Promise.all([this.worktree.getBranch(), this.worktree.getStatus()]);
const [branchResult, statusResult] = await Promise.allSettled([
this.worktree.getBranch(),
this.worktree.getStatus(),
]);
const branch = getSettledValue(branchResult);
const status = getSettledValue(statusResult);
this._branch = branch; this._branch = branch;
tooltip.appendMarkdown( tooltip.appendMarkdown(
@ -236,6 +245,9 @@ export class WorktreeNode extends ViewNode {
expand: true, expand: true,
})}`, })}`,
); );
} else if (statusResult.status === 'rejected') {
Logger.error(statusResult.reason, 'Worktree status failed');
missing = true;
} }
if (branch != null) { if (branch != null) {
@ -314,7 +326,14 @@ export class WorktreeNode extends ViewNode {
)}${indicators}\\\n\`${this.worktree.friendlyPath}\``, )}${indicators}\\\n\`${this.worktree.friendlyPath}\``,
); );
const status = await this.worktree.getStatus();
let status;
try {
status = await this.worktree.getStatus();
} catch (ex) {
Logger.error(ex, 'Worktree status failed');
missing = true;
}
if (status != null) { if (status != null) {
hasChanges = status.hasChanges; hasChanges = status.hasChanges;
tooltip.appendMarkdown( tooltip.appendMarkdown(
@ -335,6 +354,10 @@ export class WorktreeNode extends ViewNode {
tooltip.appendMarkdown(`\n\n$(loading~spin) Loading associated pull request${GlyphChars.Ellipsis}`); tooltip.appendMarkdown(`\n\n$(loading~spin) Loading associated pull request${GlyphChars.Ellipsis}`);
} }
if (missing) {
tooltip.appendMarkdown(`\n\n${GlyphChars.Warning} Unable to locate worktree path`);
}
const item = new TreeItem(this.worktree.name, TreeItemCollapsibleState.Collapsed); const item = new TreeItem(this.worktree.name, TreeItemCollapsibleState.Collapsed);
item.id = this.id; item.id = this.id;
item.description = description; item.description = description;
@ -348,7 +371,11 @@ export class WorktreeNode extends ViewNode {
? new ThemeIcon('check') ? new ThemeIcon('check')
: icon; : icon;
item.tooltip = tooltip; item.tooltip = tooltip;
item.resourceUri = hasChanges ? Uri.parse('gitlens-view://worktree/changes') : undefined;
item.resourceUri = missing
? Uri.parse('gitlens-view://worktree/missing')
: hasChanges
? Uri.parse('gitlens-view://worktree/changes')
: undefined;
return item; return item;
} }

+ 25
- 15
src/views/worktreesView.ts View File

@ -3,7 +3,7 @@ import { ProgressLocation, ThemeColor, TreeItem, TreeItemCollapsibleState, windo
import type { WorktreesViewConfig } from '../config'; import type { WorktreesViewConfig } from '../config';
import { ViewFilesLayout, ViewShowBranchComparison } from '../config'; import { ViewFilesLayout, ViewShowBranchComparison } from '../config';
import type { Colors } from '../constants'; import type { Colors } from '../constants';
import { Commands } from '../constants';
import { Commands, GlyphChars } from '../constants';
import type { Container } from '../container'; import type { Container } from '../container';
import { PlusFeatures } from '../features'; import { PlusFeatures } from '../features';
import { GitUri } from '../git/gitUri'; import { GitUri } from '../git/gitUri';
@ -100,21 +100,31 @@ export class WorktreesView extends ViewBase<'worktrees', WorktreesViewNode, Work
this.disposables.push( this.disposables.push(
window.registerFileDecorationProvider({ window.registerFileDecorationProvider({
provideFileDecoration: (uri, _token) => { provideFileDecoration: (uri, _token) => {
if (
uri.scheme !== 'gitlens-view' ||
uri.authority !== 'worktree' ||
!uri.path.includes('/changes')
) {
return undefined;
if (uri.scheme !== 'gitlens-view' || uri.authority !== 'worktree') return undefined;
const [, status] = uri.path.split('/');
switch (status) {
case 'changes':
return {
badge: '●',
color: new ThemeColor(
'gitlens.decorations.worktreeHasUncommittedChangesForegroundColor' as Colors,
),
tooltip: 'Has Uncommitted Changes',
};
case 'missing':
return {
badge: GlyphChars.Warning,
color: new ThemeColor(
'gitlens.decorations.worktreeMissingForegroundColor' satisfies Colors,
),
tooltip: '',
};
default:
return undefined;
} }
return {
badge: '●',
color: new ThemeColor(
'gitlens.decorations.worktreeView.hasUncommittedChangesForegroundColor' as Colors,
),
tooltip: 'Has Uncommitted Changes',
};
}, },
}), }),
); );

Loading…
Cancel
Save