Skip to content

Commit 51a20db

Browse files
committed
Rewrites & optimizes Git execution & log parsing
- Adds strongly typed parser interfaces and unified logging format - Replaces legacy string-based log parsing with structured parsers - Adds streaming support for large git log outputs (not fully realized yet) - Consolidates duplicate parser implementations into shared utilities - Consolidates revision resolution / revision navigation logic - Improves handling of renames, copies, and deleted files Adds `gitlens.advanced.commits.delayLoadingFileDetails` setting to delay loading file details until required Improves `diffWith` command handling & lables especially w/ comparisons
1 parent fdeae21 commit 51a20db

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+2714
-2693
lines changed

package.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5247,9 +5247,12 @@
52475247
"scope": "window",
52485248
"order": 110
52495249
},
5250-
"gitlens.advanced.useSymmetricDifferenceNotation": {
5251-
"deprecationMessage": "Deprecated. This setting is no longer used",
5252-
"markdownDescription": "Deprecated. This setting is no longer used"
5250+
"gitlens.advanced.commits.delayLoadingFileDetails": {
5251+
"type": "boolean",
5252+
"default": false,
5253+
"markdownDescription": "Specifies whether to delay loading commit file details until required. This can improve performance when opening repositories with large histories, but causes more incremental Git calls",
5254+
"scope": "window",
5255+
"order": 120
52535256
}
52545257
}
52555258
},

src/commands/browseRepoAtRevision.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,13 @@ export class BrowseRepoAtRevisionCommand extends ActiveEditorCommand {
5656
}
5757

5858
let gitUri = await GitUri.fromUri(uri);
59-
if (gitUri.sha == null) return;
59+
if (gitUri.sha == null) throw new Error('No SHA for Uri');
6060

61-
const sha = args?.before
62-
? await this.container.git.refs(gitUri.repoPath!).resolveReference(`${gitUri.sha}^`)
63-
: gitUri.sha;
64-
uri = this.container.git.getRevisionUri(gitUri.repoPath!, sha, gitUri.repoPath!);
61+
const repo = this.container.git.getRepository(gitUri.repoPath!);
62+
if (repo == null) throw new Error('No repository for Uri');
63+
64+
const sha = args?.before ? (await repo.git.revision().resolveRevision(`${gitUri.sha}^`)).sha : gitUri.sha;
65+
uri = repo.git.getRevisionUri(sha, gitUri.repoPath!);
6566
gitUri = GitUri.fromRevisionUri(uri);
6667

6768
openWorkspace(uri, {

src/commands/diffWith.ts

Lines changed: 75 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@ import type { Container } from '../container';
55
import type { GitCommit } from '../git/models/commit';
66
import { isCommit } from '../git/models/commit';
77
import { deletedOrMissing } from '../git/models/revision';
8-
import { isShaWithOptionalRevisionSuffix, isUncommitted, shortenRevision } from '../git/utils/revision.utils';
8+
import { isShaWithParentSuffix, isUncommitted, shortenRevision } from '../git/utils/revision.utils';
99
import { showGenericErrorMessage } from '../messages';
1010
import { command } from '../system/-webview/command';
1111
import { openDiffEditor } from '../system/-webview/vscode/editors';
1212
import { createMarkdownCommandLink } from '../system/commands';
1313
import { Logger } from '../system/logger';
1414
import { basename } from '../system/path';
15+
import { getSettledValue } from '../system/promise';
1516
import { GlCommandBase } from './commandBase';
1617

1718
export interface DiffWithCommandArgsRevision {
@@ -25,6 +26,7 @@ export interface DiffWithCommandArgs {
2526
rhs: DiffWithCommandArgsRevision;
2627
repoPath: string | undefined;
2728

29+
fromComparison?: boolean;
2830
line?: number;
2931
showOptions?: TextDocumentShowOptions;
3032
}
@@ -45,28 +47,16 @@ export class DiffWithCommand extends GlCommandBase {
4547
if (commit.isUncommitted) {
4648
args = {
4749
repoPath: commit.repoPath,
48-
lhs: {
49-
sha: 'HEAD',
50-
uri: commit.file.uri,
51-
},
52-
rhs: {
53-
sha: '',
54-
uri: commit.file.uri,
55-
},
50+
lhs: { sha: 'HEAD', uri: commit.file.uri },
51+
rhs: { sha: '', uri: commit.file.uri },
5652
line: line,
5753
};
5854
} else {
5955
args = {
6056
repoPath: commit.repoPath,
61-
lhs: {
62-
// Don't need to worry about verifying the previous sha, as the DiffWith command will
63-
sha: commit.unresolvedPreviousSha,
64-
uri: commit.file.originalUri ?? commit.file.uri,
65-
},
66-
rhs: {
67-
sha: commit.sha,
68-
uri: commit.file.uri,
69-
},
57+
// Don't need to worry about verifying the previous sha, as the DiffWith command will
58+
lhs: { sha: commit.unresolvedPreviousSha, uri: commit.file.originalUri ?? commit.file.uri },
59+
rhs: { sha: commit.sha, uri: commit.file.uri },
7060
line: line,
7161
};
7262
}
@@ -84,111 +74,107 @@ export class DiffWithCommand extends GlCommandBase {
8474
async execute(args?: DiffWithCommandArgs): Promise<any> {
8575
if (args?.lhs == null || args?.rhs == null) return;
8676

87-
args = {
88-
...args,
89-
lhs: { ...args.lhs },
90-
rhs: { ...args.rhs },
91-
showOptions: args.showOptions == null ? undefined : { ...args.showOptions },
92-
};
93-
94-
if (args.repoPath == null) return;
77+
const repo = args.repoPath ? this.container.git.getRepository(args.repoPath) : undefined;
78+
if (repo == null) return;
9579

9680
try {
97-
let lhsSha = args.lhs.sha;
98-
let rhsSha = args.rhs.sha;
99-
100-
[args.lhs.sha, args.rhs.sha] = await Promise.all([
101-
await this.container.git.refs(args.repoPath).resolveReference(args.lhs.sha, args.lhs.uri, {
102-
// If the ref looks like a sha, don't wait too long, since it should work
103-
timeout: isShaWithOptionalRevisionSuffix(args.lhs.sha) ? 100 : undefined,
104-
}),
105-
await this.container.git.refs(args.repoPath).resolveReference(args.rhs.sha, args.rhs.uri, {
106-
// If the ref looks like a sha, don't wait too long, since it should work
107-
timeout: isShaWithOptionalRevisionSuffix(args.rhs.sha) ? 100 : undefined,
108-
}),
81+
let {
82+
lhs: { sha: lhsSha, uri: lhsUri, title: lhsTitle },
83+
rhs: { sha: rhsSha, uri: rhsUri, title: rhsTitle },
84+
} = args;
85+
const showOptions = { viewColumn: ViewColumn.Active, ...args.showOptions };
86+
87+
let [lhsResolvedResult, rhsResolvedResult] = await Promise.allSettled([
88+
repo.git.revision().resolveRevision(lhsSha, lhsUri),
89+
repo.git.revision().resolveRevision(rhsSha, rhsUri),
10990
]);
11091

111-
if (args.lhs.sha !== deletedOrMissing) {
112-
lhsSha = args.lhs.sha;
113-
}
92+
let lhsResolved = getSettledValue(lhsResolvedResult)!;
93+
let rhsResolved = getSettledValue(rhsResolvedResult)!;
11494

115-
if (args.rhs.sha && args.rhs.sha !== deletedOrMissing) {
116-
// Ensure that the file still exists in this commit
117-
const status = await this.container.git
118-
.commits(args.repoPath)
119-
.getCommitFileStatus(args.rhs.uri, args.rhs.sha);
120-
if (status?.status === 'D') {
121-
args.rhs.sha = deletedOrMissing;
122-
} else {
123-
rhsSha = args.rhs.sha;
124-
}
95+
// If both are missing, check for renames by swapping the paths
96+
if (lhsResolved.sha === deletedOrMissing && rhsResolved.sha === deletedOrMissing) {
97+
[lhsResolvedResult, rhsResolvedResult] = await Promise.allSettled([
98+
repo?.git.revision().resolveRevision(lhsSha, rhsUri),
99+
repo?.git.revision().resolveRevision(rhsSha, lhsUri),
100+
]);
125101

126-
if (status?.status === 'A' && args.lhs.sha.endsWith('^')) {
127-
args.lhs.sha = deletedOrMissing;
102+
lhsResolved = getSettledValue(lhsResolvedResult)!;
103+
rhsResolved = getSettledValue(rhsResolvedResult)!;
104+
105+
if (lhsResolved.sha !== deletedOrMissing || rhsResolved.sha !== deletedOrMissing) {
106+
[lhsTitle, rhsTitle] = [rhsTitle, lhsTitle];
107+
[lhsUri, rhsUri] = [rhsUri, lhsUri];
128108
}
129109
}
130110

131-
const [lhs, rhs] = await Promise.all([
132-
this.container.git.getBestRevisionUri(args.repoPath, args.lhs.uri.fsPath, args.lhs.sha),
133-
this.container.git.getBestRevisionUri(args.repoPath, args.rhs.uri.fsPath, args.rhs.sha),
111+
if (rhsResolved.status === 'D') {
112+
rhsResolved.sha = deletedOrMissing;
113+
} else if (rhsResolved.status === 'R' || rhsResolved.status === 'C') {
114+
rhsUri = this.container.git.getAbsoluteUri(rhsResolved.path!, args.repoPath);
115+
} else if (rhsResolved.status === 'A' && isShaWithParentSuffix(lhsResolved.sha)) {
116+
lhsResolved.sha = deletedOrMissing;
117+
}
118+
119+
const [lhsResult, rhsResult] = await Promise.allSettled([
120+
repo.git.getBestRevisionUri(lhsUri.fsPath, lhsResolved.sha),
121+
repo.git.getBestRevisionUri(rhsUri.fsPath, rhsResolved.sha),
134122
]);
135123

136-
let rhsSuffix = shortenRevision(rhsSha, { strings: { uncommitted: 'Working Tree' } });
124+
const lhs = getSettledValue(lhsResult);
125+
const rhs = getSettledValue(rhsResult);
126+
127+
let rhsSuffix = shortenRevision(rhsResolved.revision);
137128
if (rhs == null) {
138-
if (isUncommitted(args.rhs.sha)) {
139-
rhsSuffix = 'deleted';
140-
} else if (rhsSuffix.length === 0 && args.rhs.sha === deletedOrMissing) {
141-
rhsSuffix = 'not in Working Tree';
129+
if (isUncommitted(rhsResolved.sha)) {
130+
rhsSuffix = 'Deleted';
131+
} else if (!rhsSuffix && rhsResolved.sha === deletedOrMissing) {
132+
rhsSuffix = 'Not in Working Tree';
142133
} else {
143-
rhsSuffix = `deleted${rhsSuffix.length === 0 ? '' : ` in ${rhsSuffix}`}`;
134+
rhsSuffix = `${args.fromComparison ? 'Missing' : 'Deleted'}${!rhsSuffix ? '' : ` in ${rhsSuffix}`}`;
144135
}
145136
} else if (lhs == null) {
146-
rhsSuffix = `added${rhsSuffix.length === 0 ? '' : ` in ${rhsSuffix}`}`;
137+
if (!args.fromComparison) {
138+
rhsSuffix = `Added${!rhsSuffix ? '' : ` in ${rhsSuffix}`}`;
139+
}
147140
}
148141

149-
let lhsSuffix = args.lhs.sha !== deletedOrMissing ? shortenRevision(lhsSha) : '';
150-
if (lhs == null && args.rhs.sha.length === 0) {
142+
let lhsSuffix = shortenRevision(lhsResolved.revision);
143+
if (lhsResolved.sha === deletedOrMissing) {
144+
lhsSuffix = args.fromComparison ? `Missing${!lhsSuffix ? '' : ` in ${lhsSuffix}`}` : '';
145+
}
146+
if (lhs == null && !rhsResolved.sha) {
151147
if (rhs != null) {
152-
lhsSuffix = lhsSuffix.length === 0 ? '' : `not in ${lhsSuffix}`;
148+
lhsSuffix = !lhsSuffix ? '' : `Not in ${lhsSuffix}`;
153149
rhsSuffix = '';
154150
} else {
155-
lhsSuffix = `deleted${lhsSuffix.length === 0 ? '' : ` in ${lhsSuffix}`}`;
151+
lhsSuffix = `${args.fromComparison ? 'Missing' : 'Deleted'}${!lhsSuffix ? '' : ` in ${lhsSuffix}`}`;
156152
}
157153
}
158154

159-
if (args.lhs.title == null && (lhs != null || lhsSuffix.length !== 0)) {
160-
args.lhs.title = `${basename(args.lhs.uri.fsPath)}${lhsSuffix ? ` (${lhsSuffix})` : ''}`;
161-
}
162-
if (args.rhs.title == null) {
163-
args.rhs.title = `${basename(args.rhs.uri.fsPath)}${rhsSuffix ? ` (${rhsSuffix})` : ''}`;
155+
if (lhsTitle == null && (lhs != null || lhsSuffix)) {
156+
lhsTitle = `${basename(args.lhs.uri.fsPath)}${lhsSuffix ? ` (${lhsSuffix})` : ''}`;
164157
}
158+
rhsTitle ??= `${basename(args.rhs.uri.fsPath)}${rhsSuffix ? ` (${rhsSuffix})` : ''}`;
165159

166160
const title =
167-
args.lhs.title != null && args.rhs.title != null
168-
? `${args.lhs.title} ${GlyphChars.ArrowLeftRightLong} ${args.rhs.title}`
169-
: args.lhs.title ?? args.rhs.title;
170-
171-
if (args.showOptions == null) {
172-
args.showOptions = {};
173-
}
174-
175-
if (args.showOptions.viewColumn == null) {
176-
args.showOptions.viewColumn = ViewColumn.Active;
177-
}
161+
lhsTitle != null && rhsTitle != null
162+
? `${lhsTitle} ${GlyphChars.ArrowLeftRightLong} ${rhsTitle}`
163+
: lhsTitle ?? rhsTitle;
178164

179-
if (args.line != null && args.line !== 0) {
180-
args.showOptions.selection = new Range(args.line, 0, args.line, 0);
165+
if (args.line) {
166+
showOptions.selection = new Range(args.line, 0, args.line, 0);
181167
}
182168

183169
await openDiffEditor(
184-
lhs ?? this.container.git.getRevisionUri(args.repoPath, deletedOrMissing, args.lhs.uri.fsPath),
185-
rhs ?? this.container.git.getRevisionUri(args.repoPath, deletedOrMissing, args.rhs.uri.fsPath),
170+
lhs ?? repo.git.getRevisionUri(deletedOrMissing, args.lhs.uri.fsPath),
171+
rhs ?? repo.git.getRevisionUri(deletedOrMissing, args.rhs.uri.fsPath),
186172
title,
187173
args.showOptions,
188174
);
189175
} catch (ex) {
190-
Logger.error(ex, 'DiffWithCommand', 'getVersionedFile');
191-
void showGenericErrorMessage('Unable to open compare');
176+
Logger.error(ex, 'DiffWithCommand');
177+
void showGenericErrorMessage('Unable to open comparison');
192178
}
193179
}
194180
}

src/commands/git/show.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,6 @@ export class ShowGitCommand extends QuickCommand<State> {
152152
repoPath: state.repo.path,
153153
commits: new Map<string, GitCommit | GitStashCommit>(),
154154
sha: undefined,
155-
range: undefined,
156155
count: 0,
157156
limit: undefined,
158157
hasMore: false,

src/commands/git/worktree.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,9 +1202,7 @@ export class WorktreeGitCommand extends QuickCommand<State> {
12021202
}
12031203

12041204
if (!isSha(state.changes.baseSha)) {
1205-
const sha = await state.repo.git
1206-
.refs()
1207-
.resolveReference(state.changes.baseSha, undefined, { force: true });
1205+
const sha = (await state.repo.git.revision().resolveRevision(state.changes.baseSha)).sha;
12081206
if (sha != null) {
12091207
state.changes.baseSha = sha;
12101208
}

src/commands/openOnRemote.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,11 @@ export class OpenOnRemoteCommand extends GlCommandBase {
7070
const file = await commit.findFile(fileName);
7171
if (file?.status === 'D') {
7272
// Resolve to the previous commit to that file
73-
resource.sha = await this.container.git
74-
.refs(commit.repoPath)
75-
.resolveReference(`${commit.sha}^`, fileName);
73+
resource.sha = (
74+
await this.container.git
75+
.revision(commit.repoPath)
76+
.resolveRevision(`${commit.sha}^`, fileName)
77+
).sha;
7678
} else {
7779
resource.sha = commit.sha;
7880
}

src/config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,9 @@ export interface AdvancedConfig {
190190
readonly gitPath: boolean;
191191
};
192192
readonly commitOrdering: 'date' | 'author-date' | 'topo' | null;
193+
readonly commits: {
194+
readonly delayLoadingFileDetails: boolean;
195+
};
193196
readonly externalDiffTool: string | null;
194197
readonly externalDirectoryDiffTool: string | null;
195198
readonly fileHistoryFollowsRenames: boolean;

0 commit comments

Comments
 (0)