Skip to content

Commit 9176797

Browse files
committed
Adds git notes support via ${notes} format token - closes #2432
- Adds ${notes} token to CommitFormatter for inline blame, hovers, and status bar - Adds %N to log parser commits mapping for notes from git log - Adds getCommitNotes() sub-provider method using git notes show - Adds ensureNotes() on GitCommit with @gate() and cancellation support - Adds notes to settings preview and settings HTML token documentation - Updates logParser tests for new notes field
1 parent e24eda0 commit 9176797

File tree

12 files changed

+79
-5
lines changed

12 files changed

+79
-5
lines changed

CHANGELOG.md

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

99
### Added
1010

11+
- Adds support for displaying git notes in blame annotations, hovers, and status bar via the `${notes}` format token ([#2432](https://github.com/gitkraken/vscode-gitlens/issues/2432)) thanks to [PR #5085](5085) by Matt M ([@Mattwmaster58](https://github.com/Mattwmaster58))
1112
- Adds a conflict files panel to the _Interactive Rebase_ editor — shows conflicting files with per-file conflict counts, conflict status indicators, and actions to view current or incoming changes ([#5040](https://github.com/gitkraken/vscode-gitlens/issues/5040))
1213
- Adds branch activity dates to the _Home_ view recents — sorts by most recent activity and displays the most relevant activity label ([#5034](https://github.com/gitkraken/vscode-gitlens/issues/5034))
1314

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,7 @@ A big thanks to the people that have contributed to this project 🙏❤️:
448448
- JounQin ([@JounQin](https://github.com/JounQin)) — [contributions](https://github.com/gitkraken/vscode-gitlens/commits?author=JounQin)
449449
- Noritaka Kobayashi ([@noritaka1166](https://github.com/noritaka1166)) — [contributions](https://github.com/gitkraken/vscode-gitlens/commits?author=noritaka1166)
450450
- Daniel Asher ([@danielasher115](https://github.com/danielasher115)) — [contributions](https://github.com/gitkraken/vscode-gitlens/commits?author=danielasher115)
451+
- Matt M ([@Mattwmaster58](https://github.com/Mattwmaster58)) — [contributions](https://github.com/gitkraken/vscode-gitlens/commits?author=Mattwmaster58)
451452

452453
Also special thanks to the people that have provided support, testing, brainstorming, etc:
453454

src/annotations/lineAnnotationController.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,19 @@ export class LineAnnotationController implements Disposable {
287287
? this.container.git.getRepositoryService(repoPath).getBranchesAndTagsTipsLookup()
288288
: undefined;
289289

290+
const getNotesForLines = !uncommittedOnly && repoPath != null && CommitFormatter.has(cfg.format, 'notes');
291+
if (getNotesForLines) {
292+
const seen = new Set<string>();
293+
await Promise.allSettled(
294+
Array.from(lines.values(), state => {
295+
if (state.commit.isUncommitted || seen.has(state.commit.ref)) return Promise.resolve();
296+
seen.add(state.commit.ref);
297+
return state.commit.ensureNotes(cancellation);
298+
}),
299+
);
300+
}
301+
if (cancellation.isCancellationRequested) return;
302+
290303
async function updateDecorations(
291304
container: Container,
292305
editor: TextEditor,

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,20 @@ export class CommitsGitSubProvider implements GitCommitsSubProvider {
114114
return log.commits.get(rev) ?? first(log.commits.values());
115115
}
116116

117+
@debug({ exit: true })
118+
async getCommitNotes(repoPath: string, rev: string, cancellation?: CancellationToken): Promise<string | undefined> {
119+
const result = await this.git.exec(
120+
{ cwd: repoPath, cancellation: cancellation, errors: 'ignore' },
121+
'notes',
122+
'show',
123+
rev,
124+
);
125+
if (result.cancelled || cancellation?.isCancellationRequested) throw new CancellationError();
126+
127+
const data = result.stdout.trim();
128+
return data || undefined;
129+
}
130+
117131
@debug({ exit: true })
118132
async getCommitCount(repoPath: string, rev: string, cancellation?: CancellationToken): Promise<number | undefined> {
119133
const result = await this.git.exec(
@@ -1394,6 +1408,7 @@ function createCommit(
13941408
) {
13951409
const message = c.message.trim();
13961410
const index = message.indexOf('\n');
1411+
const notes = c.notes?.trim() || null;
13971412

13981413
return new GitCommit(
13991414
container,
@@ -1416,6 +1431,10 @@ function createCommit(
14161431
c.stats,
14171432
undefined,
14181433
c.tips?.split(' '),
1434+
undefined,
1435+
undefined,
1436+
undefined,
1437+
notes,
14191438
);
14201439
}
14211440

src/git/formatters/commitFormatter.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ export interface CommitFormatOptions extends FormatOptions {
119119
id?: TokenOptions;
120120
link?: TokenOptions;
121121
message?: TokenOptions;
122+
notes?: TokenOptions;
122123
pullRequest?: TokenOptions;
123124
pullRequestAgo?: TokenOptions;
124125
pullRequestAgoOrDate?: TokenOptions;
@@ -715,6 +716,10 @@ export class CommitFormatter extends Formatter<GitCommit, CommitFormatOptions> {
715716
);
716717
}
717718

719+
get notes(): string {
720+
return this._padOrTruncate(this._item.notes ?? '', this._options.tokenOptions.notes);
721+
}
722+
718723
get id(): string {
719724
const sha = this._padOrTruncate(this._item.shortSha ?? '', this._options.tokenOptions.id);
720725
if (this._options.outputFormat !== 'plaintext' && this._options.unpublished) {

src/git/gitProvider.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@ export interface GitCommitsSubProvider {
339339
options?: { firstIfNotFound?: boolean | undefined },
340340
cancellation?: CancellationToken,
341341
): Promise<GitCommit | undefined>;
342+
getCommitNotes?(repoPath: string, rev: string, cancellation?: CancellationToken): Promise<string | undefined>;
342343
getIncomingActivity?(
343344
repoPath: string,
344345
options?: IncomingActivityOptions,

src/git/models/commit.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/* eslint-disable @typescript-eslint/no-restricted-imports -- TODO need to deal with sharing rich class shapes to webviews */
2+
import type { CancellationToken } from 'vscode';
23
import { Uri } from 'vscode';
34
import type { EnrichedAutolink } from '../../autolinks/models/autolinks.js';
45
import { getAvatarUri, getCachedAvatarUri } from '../../avatars.js';
@@ -88,11 +89,13 @@ export class GitCommit implements GitRevisionReference {
8889
stashName?: string | undefined,
8990
stashOnRef?: string | undefined,
9091
parentTimestamps?: GitStashParentInfo[] | undefined,
92+
notes?: string | null | undefined,
9193
) {
9294
this.ref = sha;
9395
this.shortSha = sha.substring(0, this.container.CommitShaFormatting.length);
9496
this.tips = tips;
9597
this.parentTimestamps = parentTimestamps;
98+
this._notes = notes;
9699

97100
if (stashName) {
98101
this.refType = 'stash';
@@ -132,6 +135,11 @@ export class GitCommit implements GitRevisionReference {
132135
this.lines = ensureArray(lines) ?? [];
133136
}
134137

138+
private _notes: string | undefined | null;
139+
get notes(): string | undefined {
140+
return this._notes ?? undefined;
141+
}
142+
135143
get date(): Date {
136144
return this.container.CommitDateFormatting.dateSource === 'committed' ? this.committer.date : this.author.date;
137145
}
@@ -588,6 +596,14 @@ export class GitCommit implements GitRevisionReference {
588596
return this.author.getCachedAvatarUri(options);
589597
}
590598

599+
@gate()
600+
async ensureNotes(cancellation?: CancellationToken): Promise<void> {
601+
if (this.isUncommitted || this._notes !== undefined) return;
602+
603+
const svc = this.container.git.getRepositoryService(this.repoPath);
604+
this._notes = (await svc.commits.getCommitNotes?.(this.sha, cancellation)) ?? null;
605+
}
606+
591607
async getSignature(): Promise<CommitSignature | undefined> {
592608
if (this.isUncommitted) return undefined;
593609
if (this._signature === null) return undefined;
@@ -745,6 +761,7 @@ export class GitCommit implements GitRevisionReference {
745761
this.stashName,
746762
this.stashOnRef,
747763
this.getChangedValue(changes.parentTimestamps, this.parentTimestamps),
764+
this._notes,
748765
) as T;
749766
}
750767

@@ -771,6 +788,7 @@ export class GitCommit implements GitRevisionReference {
771788
this.stashName,
772789
this.stashOnRef,
773790
this.parentTimestamps,
791+
this._notes,
774792
) as T);
775793
}
776794

src/git/parsers/__tests__/logParser.test.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ suite('Log Parser - File Parsing Test Suite', () => {
2323
}
2424

2525
// Helper to create a mock commit record with files
26-
// Format from commitsMapping: sha, author, authorEmail, authorDate, committer, committerEmail, committerDate, parents, tips, message
26+
// Format from commitsMapping: sha, author, authorEmail, authorDate, committer, committerEmail, committerDate, parents, tips, notes, message
2727
// The file content comes as a separate field AFTER the message field
2828
function createCommitRecord(
2929
sha: string,
@@ -35,8 +35,8 @@ suite('Log Parser - File Parsing Test Suite', () => {
3535
// File content: --raw lines first, then --numstat lines, separated by newlines
3636
const fileContent = [...rawLines, ...numstatLines].join('\n');
3737

38-
// Format: recordSep + 10 mapped fields + 1 file content field, all separated by fieldSep
39-
// When fieldCount === keys.length (10), the parser reads the file content field
38+
// Format: recordSep + 11 mapped fields + 1 file content field, all separated by fieldSep
39+
// When fieldCount === keys.length (11), the parser reads the file content field
4040
const fields = [
4141
sha, // sha (field 0)
4242
'Test Author', // author (field 1)
@@ -47,8 +47,9 @@ suite('Log Parser - File Parsing Test Suite', () => {
4747
'1234567890', // committerDate (field 6)
4848
'', // parents (field 7)
4949
'', // tips (field 8)
50-
'Test commit message', // message (field 9)
51-
fileContent, // file content (field 10, parsed when fieldCount === 10)
50+
'', // notes (field 9)
51+
'Test commit message', // message (field 10)
52+
fileContent, // file content (field 11, parsed when fieldCount === 11)
5253
];
5354
return recordSep + fields.join(fieldSep);
5455
}

src/git/parsers/logParser.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const commitsMapping = {
1414
committerDate: '%ct',
1515
parents: '%P',
1616
tips: '%D',
17+
notes: '%N',
1718
message: '%B',
1819
};
1920

src/hovers/hovers.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,13 +245,17 @@ export async function detailsMessage(
245245
!commit.isUncommitted &&
246246
CommitFormatter.has(options.format, 'signature');
247247

248+
const showNotes =
249+
!commit.isUncommitted && commit.notes === undefined && CommitFormatter.has(options.format, 'notes');
250+
248251
const [
249252
enrichedAutolinksResult,
250253
prResult,
251254
presenceResult,
252255
previousLineComparisonUrisResult,
253256
_fullDetailsResult,
254257
signedResult,
258+
_notesResult,
255259
] = await Promise.allSettled([
256260
enhancedAutolinks
257261
? pauseOnCancelOrTimeoutMapTuplePromise(
@@ -279,6 +283,7 @@ export async function detailsMessage(
279283
: undefined,
280284
commit.message == null ? commit.ensureFullDetails() : undefined,
281285
showSignature ? commit.isSigned() : undefined,
286+
showNotes ? commit.ensureNotes(options?.cancellation) : undefined,
282287
]);
283288

284289
if (options?.cancellation?.isCancellationRequested) return undefined;

0 commit comments

Comments
 (0)