Skip to content

Commit f561689

Browse files
committed
Updates Git version feature checks
- Adds warning message when worktrees are not supported
1 parent 02292d3 commit f561689

File tree

9 files changed

+74
-22
lines changed

9 files changed

+74
-22
lines changed

contributions.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13987,7 +13987,7 @@
1398713987
},
1398813988
{
1398913989
"contents": "[Create Worktree...](command:gitlens.views.createWorktree)",
13990-
"when": "gitlens:views:scm:grouped:view == worktrees && !gitlens:plus:required"
13990+
"when": "gitlens:views:scm:grouped:view == worktrees && !gitlens:plus:required && !gitlens:feature:unsupported:git:worktrees"
1399113991
},
1399213992
{
1399313993
"contents": "[Resend Verification Email](command:gitlens.plus.resendVerification?%7B%22source%22%3A%22worktrees%22%7D)\n\nYou must verify your email before you can continue or [recheck Status](command:gitlens.plus.validate?%7B%22source%22%3A%22worktrees%22%7D).",
@@ -14024,6 +14024,10 @@
1402414024
{
1402514025
"contents": "Use on privately-hosted repos require [GitLens Pro](https://help.gitkraken.com/gitlens/gitlens-community-vs-gitlens-pro/).",
1402614026
"when": "gitlens:views:scm:grouped:view == worktrees && gitlens:plus:state != 6"
14027+
},
14028+
{
14029+
"contents": "⚠ Worktrees are not supported by your version of Git. Please upgrade to a more recent version.",
14030+
"when": "gitlens:views:scm:grouped:view == worktrees && !gitlens:plus:required && gitlens:feature:unsupported:git:worktrees"
1402714031
}
1402814032
]
1402914033
},
@@ -14121,7 +14125,7 @@
1412114125
},
1412214126
{
1412314127
"contents": "[Create Worktree...](command:gitlens.views.createWorktree)",
14124-
"when": "!gitlens:plus:required"
14128+
"when": "!gitlens:plus:required && !gitlens:feature:unsupported:git:worktrees"
1412514129
},
1412614130
{
1412714131
"contents": "[Resend Verification Email](command:gitlens.plus.resendVerification?%7B%22source%22%3A%22worktrees%22%7D)\n\nYou must verify your email before you can continue or [recheck Status](command:gitlens.plus.validate?%7B%22source%22%3A%22worktrees%22%7D).",
@@ -14158,6 +14162,10 @@
1415814162
{
1415914163
"contents": "Use on privately-hosted repos require [GitLens Pro](https://help.gitkraken.com/gitlens/gitlens-community-vs-gitlens-pro/).",
1416014164
"when": "gitlens:plus:state != 6"
14165+
},
14166+
{
14167+
"contents": "⚠ Worktrees are not supported by your version of Git. Please upgrade to a more recent version.",
14168+
"when": "!gitlens:plus:required && gitlens:feature:unsupported:git:worktrees"
1416114169
}
1416214170
]
1416314171
}

package.json

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21040,7 +21040,7 @@
2104021040
{
2104121041
"view": "gitlens.views.scm.grouped",
2104221042
"contents": "[Create Worktree...](command:gitlens.views.createWorktree)",
21043-
"when": "gitlens:views:scm:grouped:view == worktrees && !gitlens:plus:required"
21043+
"when": "gitlens:views:scm:grouped:view == worktrees && !gitlens:plus:required && !gitlens:feature:unsupported:git:worktrees"
2104421044
},
2104521045
{
2104621046
"view": "gitlens.views.scm.grouped",
@@ -21087,6 +21087,11 @@
2108721087
"contents": "Use on privately-hosted repos require [GitLens Pro](https://help.gitkraken.com/gitlens/gitlens-community-vs-gitlens-pro/).",
2108821088
"when": "gitlens:views:scm:grouped:view == worktrees && gitlens:plus:state != 6"
2108921089
},
21090+
{
21091+
"view": "gitlens.views.scm.grouped",
21092+
"contents": "⚠ Worktrees are not supported by your version of Git. Please upgrade to a more recent version.",
21093+
"when": "gitlens:views:scm:grouped:view == worktrees && !gitlens:plus:required && gitlens:feature:unsupported:git:worktrees"
21094+
},
2109021095
{
2109121096
"view": "gitlens.views.searchAndCompare",
2109221097
"contents": "Search for commits by [message](command:gitlens.views.searchAndCompare.searchCommits?%7B%22search%22%3A%7B%22query%22%3A%22message%3A%22%7D%2C%22prefillOnly%22%3Atrue%7D), [author](command:gitlens.views.searchAndCompare.searchCommits?%7B%22search%22%3A%7B%22query%22%3A%22author%3A%22%7D%2C%22prefillOnly%22%3Atrue%7D), [SHA](command:gitlens.views.searchAndCompare.searchCommits?%7B%22search%22%3A%7B%22query%22%3A%22commit%3A%22%7D%2C%22prefillOnly%22%3Atrue%7D), [file](command:gitlens.views.searchAndCompare.searchCommits?%7B%22search%22%3A%7B%22query%22%3A%22file%3A%22%7D%2C%22prefillOnly%22%3Atrue%7D), or [changes](command:gitlens.views.searchAndCompare.searchCommits?%7B%22search%22%3A%7B%22query%22%3A%22change%3A%22%7D%2C%22prefillOnly%22%3Atrue%7D)\n\n[Search Commits...](command:gitlens.views.searchAndCompare.searchCommits)",
@@ -21128,7 +21133,7 @@
2112821133
{
2112921134
"view": "gitlens.views.worktrees",
2113021135
"contents": "[Create Worktree...](command:gitlens.views.createWorktree)",
21131-
"when": "!gitlens:plus:required"
21136+
"when": "!gitlens:plus:required && !gitlens:feature:unsupported:git:worktrees"
2113221137
},
2113321138
{
2113421139
"view": "gitlens.views.worktrees",
@@ -21174,6 +21179,11 @@
2117421179
"view": "gitlens.views.worktrees",
2117521180
"contents": "Use on privately-hosted repos require [GitLens Pro](https://help.gitkraken.com/gitlens/gitlens-community-vs-gitlens-pro/).",
2117621181
"when": "gitlens:plus:state != 6"
21182+
},
21183+
{
21184+
"view": "gitlens.views.worktrees",
21185+
"contents": "⚠ Worktrees are not supported by your version of Git. Please upgrade to a more recent version.",
21186+
"when": "!gitlens:plus:required && gitlens:feature:unsupported:git:worktrees"
2117721187
}
2117821188
],
2117921189
"views": {

src/constants.context.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Uri } from 'vscode';
22
import type { AnnotationStatus, Keys } from './constants';
33
import type { SubscriptionPlanId, SubscriptionState } from './constants.subscription';
44
import type { CustomEditorTypes, GroupableTreeViewTypes, WebviewTypes, WebviewViewTypes } from './constants.views';
5+
import type { Features } from './features';
56
import type { PromoKeys } from './plus/gk/models/promo';
67
import type { WalkthroughContextKeys } from './telemetry/walkthroughStateProvider';
78

@@ -54,6 +55,7 @@ export type ContextKeys = {
5455
'gitlens:vsls': boolean | 'host' | 'guest';
5556
'gitlens:window:annotated': AnnotationStatus;
5657
} & Record<`gitlens:action:${string}`, number> &
58+
Record<`gitlens:feature:unsupported:${Features}`, boolean> &
5759
Record<`gitlens:key:${Keys}`, boolean> &
5860
Record<`gitlens:views:scm:grouped:views:${GroupableTreeViewTypes}`, boolean> &
5961
Record<`gitlens:webview:${WebviewTypes | CustomEditorTypes}:visible`, boolean> &

src/env/node/git/git.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type { CancellationToken, OutputChannel } from 'vscode';
77
import { env, Uri, window, workspace } from 'vscode';
88
import { hrtime } from '@env/hrtime';
99
import { GlyphChars } from '../../../constants';
10-
import type { GitFeature } from '../../../features';
10+
import type { FilteredGitFeatures, GitFeatureOrPrefix, GitFeatures } from '../../../features';
1111
import { gitFeaturesByVersion } from '../../../features';
1212
import type { GitCommandOptions, GitSpawnOptions } from '../../../git/commandOptions';
1313
import { GitErrorHandling } from '../../../git/commandOptions';
@@ -364,7 +364,7 @@ export class Git {
364364
return (await this.getLocation()).path;
365365
}
366366

367-
async ensureSupports(feature: GitFeature, prefix: string, suffix: string): Promise<void> {
367+
async ensureSupports(feature: GitFeatures, prefix: string, suffix: string): Promise<void> {
368368
const version = gitFeaturesByVersion.get(feature);
369369
if (version == null) return;
370370

@@ -376,7 +376,7 @@ export class Git {
376376
);
377377
}
378378

379-
supports(feature: GitFeature): boolean | Promise<boolean> {
379+
supports(feature: GitFeatures): boolean | Promise<boolean> {
380380
const version = gitFeaturesByVersion.get(feature);
381381
if (version == null) return true;
382382

@@ -385,6 +385,19 @@ export class Git {
385385
: this.version().then(v => compare(fromString(v), fromString(version)) !== -1);
386386
}
387387

388+
supported<T extends GitFeatureOrPrefix>(feature: T): FilteredGitFeatures<T>[] | Promise<FilteredGitFeatures<T>[]> {
389+
function supportedCore(gitVersion: string): FilteredGitFeatures<T>[] {
390+
return [...gitFeaturesByVersion]
391+
.filter(([f, v]) => f.startsWith(feature) && compare(fromString(gitVersion), v) !== -1)
392+
.map(([f]) => f as FilteredGitFeatures<T>);
393+
}
394+
395+
if (this._gitLocation == null) {
396+
return this.version().then(v => supportedCore(v));
397+
}
398+
return supportedCore(this._gitLocation.version);
399+
}
400+
388401
async version(): Promise<string> {
389402
return (await this.getLocation()).version;
390403
}

src/env/node/git/localGitProvider.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ import {
6060
} from '../../../messages';
6161
import { asRepoComparisonKey } from '../../../repositories';
6262
import { configuration } from '../../../system/-webview/configuration';
63+
import { setContext } from '../../../system/-webview/context';
6364
import { getBestPath, isFolderUri, relative, splitPath } from '../../../system/-webview/path';
6465
import { gate } from '../../../system/decorators/-webview/gate';
6566
import { debug, log } from '../../../system/decorators/log';
@@ -476,6 +477,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
476477
break;
477478
}
478479

480+
void setContext(`gitlens:feature:unsupported:${feature}`, !supported);
479481
this._supportedFeatures.set(feature, supported);
480482
return supported;
481483
}

src/env/node/git/sub-providers/worktrees.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export class WorktreesGitSubProvider implements GitWorktreesSubProvider {
9393
@log()
9494
async getWorktrees(repoPath: string): Promise<GitWorktree[]> {
9595
await this.git.ensureSupports(
96-
'git:worktrees:list',
96+
'git:worktrees',
9797
'Displaying worktrees',
9898
' Please install a more recent version of Git and try again.',
9999
);
@@ -165,7 +165,7 @@ export class WorktreesGitSubProvider implements GitWorktreesSubProvider {
165165
const scope = getLogScope();
166166

167167
await this.git.ensureSupports(
168-
'git:worktrees:delete',
168+
'git:worktrees',
169169
'Deleting worktrees',
170170
' Please install a more recent version of Git and try again.',
171171
);

src/features.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { RequiredSubscriptionPlans, Subscription } from './plus/gk/models/s
55
import { capitalize } from './system/string';
66

77
// GitFeature's must start with `git:` to be recognized in all usages
8-
export type GitFeature =
8+
export type GitFeatures =
99
| 'git:for-each-ref:worktreePath'
1010
| 'git:ignoreRevsFile'
1111
| 'git:merge-tree'
@@ -17,10 +17,20 @@ export type GitFeature =
1717
| 'git:status:find-renames'
1818
| 'git:status:porcelain-v2'
1919
| 'git:worktrees'
20-
| 'git:worktrees:delete'
21-
| 'git:worktrees:list';
20+
| 'git:worktrees';
2221

23-
export const gitFeaturesByVersion = new Map<GitFeature, string>([
22+
type ExtractPrefix<T> = T extends `${infer Prefix}:${infer Rest}`
23+
? Rest extends `${infer SubPrefix}:${string}`
24+
? T | `${Prefix}:${SubPrefix}` | Prefix
25+
: T | Prefix
26+
: never;
27+
28+
export type GitFeatureOrPrefix = ExtractPrefix<GitFeatures>;
29+
export type FilteredGitFeatures<T extends GitFeatureOrPrefix> = T extends GitFeatures
30+
? T
31+
: Extract<GitFeatures, T | `${T}:${string}`>;
32+
33+
export const gitFeaturesByVersion = new Map<GitFeatures, string>([
2434
['git:for-each-ref:worktreePath', '2.23'],
2535
['git:ignoreRevsFile', '2.23'],
2636
['git:merge-tree', '2.33'],
@@ -32,11 +42,9 @@ export const gitFeaturesByVersion = new Map<GitFeature, string>([
3242
['git:status:find-renames', '2.18'],
3343
['git:status:porcelain-v2', '2.11'],
3444
['git:worktrees', '2.17.0'],
35-
['git:worktrees:delete', '2.17.0'],
36-
['git:worktrees:list', '2.7.6'],
3745
]);
3846

39-
export type Features = 'stashes' | 'timeline' | GitFeature;
47+
export type Features = 'stashes' | 'timeline' | GitFeatures;
4048

4149
export type FeatureAccess =
4250
| {

src/plus/integrations/providers/github/githubGitProvider.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,12 +243,18 @@ export class GitHubGitProvider implements GitProvider, Disposable {
243243
}
244244

245245
async supports(feature: Features): Promise<boolean> {
246+
let supported;
246247
switch (feature) {
247248
case 'timeline' satisfies Features:
248-
return true;
249+
supported = true;
250+
break;
249251
default:
250-
return false;
252+
supported = false;
253+
break;
251254
}
255+
256+
void setContext(`gitlens:feature:unsupported:${feature}`, !supported);
257+
return supported;
252258
}
253259

254260
async visibility(repoPath: string): Promise<[visibility: RepositoryVisibility, cacheKey: string | undefined]> {

src/views/worktreesView.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,7 @@ import { registerViewCommand } from './viewCommands';
2323

2424
export class WorktreesRepositoryNode extends RepositoryFolderNode<WorktreesView, WorktreesNode> {
2525
getChildren(): Promise<ViewNode[]> {
26-
if (this.child == null) {
27-
this.child = new WorktreesNode(this.uri, this.view, this, this.repo);
28-
}
29-
26+
this.child ??= new WorktreesNode(this.uri, this.view, this, this.repo);
3027
return this.child.getChildren();
3128
}
3229

@@ -69,6 +66,12 @@ export class WorktreesViewNode extends RepositoriesSubscribeableNode<WorktreesVi
6966
return [];
7067
}
7168

69+
const repo = this.view.container.git.getBestRepositoryOrFirst();
70+
if (repo != null && !(await repo.git.supports('git:worktrees'))) {
71+
// this.view.message = 'Worktrees are not supported by your version of Git.';
72+
return [];
73+
}
74+
7275
if (configuration.get('views.collapseWorktreesWhenPossible')) {
7376
const grouped = await groupRepositories(repositories);
7477
repositories = [...grouped.keys()];

0 commit comments

Comments
 (0)