Skip to content

Commit 084f580

Browse files
committed
Adds opening/showing specific commit in the Graph
- Adds Show Commit in Graph to the Commit Details view Attempts to fix Graph data paging using `--boundary`
1 parent b305092 commit 084f580

File tree

15 files changed

+237
-44
lines changed

15 files changed

+237
-44
lines changed

src/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ export const enum Commands {
163163
SearchCommitsInView = 'gitlens.views.searchAndCompare.searchCommits',
164164
SetViewsLayout = 'gitlens.setViewsLayout',
165165
ShowBranchesView = 'gitlens.showBranchesView',
166+
ShowCommitInGraph = 'gitlens.showCommitInGraph',
166167
ShowCommitInView = 'gitlens.showCommitInView',
167168
ShowCommitsInView = 'gitlens.showCommitsInView',
168169
ShowCommitsView = 'gitlens.showCommitsView',

src/env/node/git/git.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,20 @@ export class Git {
769769
);
770770
}
771771

772+
log2(repoPath: string, ref: string | undefined, ...args: unknown[]) {
773+
const params = ['log', ...args];
774+
775+
if (ref && !GitRevision.isUncommittedStaged(ref)) {
776+
params.push(ref);
777+
}
778+
779+
return this.git<string>(
780+
{ cwd: repoPath, configs: ['-c', 'diff.renameLimit=0', '-c', 'log.showSignature=false'] },
781+
...params,
782+
'--',
783+
);
784+
}
785+
772786
log__file(
773787
repoPath: string,
774788
fileName: string,

src/env/node/git/localGitProvider.ts

Lines changed: 153 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,8 +1609,75 @@ export class LocalGitProvider implements GitProvider, Disposable {
16091609
ref?: string;
16101610
},
16111611
): Promise<GitGraph> {
1612+
const scope = getLogScope();
1613+
1614+
let getLogForRefFn;
1615+
if (options?.ref != null) {
1616+
async function getLogForRef(this: LocalGitProvider): Promise<GitLog | undefined> {
1617+
let log;
1618+
1619+
const parser = GitLogParser.create<{ sha: string; date: string }>({ sha: '%H', date: '%ct' });
1620+
const data = await this.git.log(repoPath, options?.ref, { argsOrFormat: parser.arguments, limit: 0 });
1621+
1622+
let commit = first(parser.parse(data));
1623+
if (commit != null) {
1624+
log = await this.getLog(repoPath, {
1625+
all: options!.mode !== 'single',
1626+
ordering: 'date',
1627+
limit: 0,
1628+
extraArgs: [`--since="${Number(commit.date)}"`, '--boundary'],
1629+
});
1630+
1631+
let found = log?.commits.has(commit.sha) ?? false;
1632+
if (!found) {
1633+
Logger.debug(scope, `Could not find commit ${options!.ref}`);
1634+
1635+
debugger;
1636+
}
1637+
1638+
if (log?.more != null) {
1639+
const defaultItemLimit = configuration.get('graph.defaultItemLimit');
1640+
if (!found || log.commits.size < defaultItemLimit) {
1641+
Logger.debug(scope, 'Loading next page...');
1642+
1643+
log = await log.more(
1644+
(log.commits.size < defaultItemLimit
1645+
? defaultItemLimit
1646+
: configuration.get('graph.pageItemLimit')) ?? options?.limit,
1647+
);
1648+
// We need to clear the "pagedCommits", since we want to return the entire set
1649+
if (log != null) {
1650+
(log as Mutable<typeof log>).pagedCommits = undefined;
1651+
}
1652+
1653+
found = log?.commits.has(commit.sha) ?? false;
1654+
if (!found) {
1655+
Logger.debug(scope, `Still could not find commit ${options!.ref}`);
1656+
commit = undefined;
1657+
1658+
debugger;
1659+
}
1660+
}
1661+
}
1662+
1663+
if (!found) {
1664+
commit = undefined;
1665+
}
1666+
1667+
options!.ref = commit?.sha;
1668+
}
1669+
return (
1670+
log ??
1671+
this.getLog(repoPath, { all: options?.mode !== 'single', ordering: 'date', limit: options?.limit })
1672+
);
1673+
}
1674+
1675+
getLogForRefFn = getLogForRef;
1676+
}
1677+
16121678
const [logResult, stashResult, remotesResult] = await Promise.allSettled([
1613-
this.getLog(repoPath, { all: true, ordering: 'date', limit: options?.limit }),
1679+
getLogForRefFn?.call(this) ??
1680+
this.getLog(repoPath, { all: options?.mode !== 'single', ordering: 'date', limit: options?.limit }),
16141681
this.getStash(repoPath),
16151682
this.getRemotes(repoPath),
16161683
]);
@@ -1632,9 +1699,10 @@ export class LocalGitProvider implements GitProvider, Disposable {
16321699
stash: GitStash | undefined,
16331700
remotes: GitRemote[] | undefined,
16341701
options?: {
1635-
ref?: string;
1636-
mode?: 'single' | 'local' | 'all';
16371702
branch?: string;
1703+
limit?: number;
1704+
mode?: 'single' | 'local' | 'all';
1705+
ref?: string;
16381706
},
16391707
): Promise<GitGraph> {
16401708
if (log == null) {
@@ -1763,6 +1831,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
17631831
more: log.hasMore,
17641832
},
17651833
rows: rows,
1834+
sha: options?.ref,
17661835

17671836
more: async (limit: number | { until: string } | undefined): Promise<GitGraph | undefined> => {
17681837
const moreLog = await log.more?.(limit);
@@ -2256,23 +2325,79 @@ export class LocalGitProvider implements GitProvider, Disposable {
22562325
ref?: string;
22572326
since?: number | string;
22582327
until?: number | string;
2328+
extraArgs?: string[];
22592329
},
22602330
): Promise<GitLog | undefined> {
22612331
const scope = getLogScope();
22622332

2263-
const limit = options?.limit ?? configuration.get('advanced.maxListItems') ?? 0;
2264-
22652333
try {
2334+
const limit = options?.limit ?? configuration.get('advanced.maxListItems') ?? 0;
2335+
const merges = options?.merges == null ? true : options.merges;
2336+
const ordering = options?.ordering ?? configuration.get('advanced.commitOrdering');
2337+
const similarityThreshold = configuration.get('advanced.similarityThreshold');
2338+
2339+
const args = [
2340+
`--format=${options?.all ? GitLogParser.allFormat : GitLogParser.defaultFormat}`,
2341+
'--name-status',
2342+
'--full-history',
2343+
`-M${similarityThreshold == null ? '' : `${similarityThreshold}%`}`,
2344+
'-m',
2345+
];
2346+
if (options?.all) {
2347+
args.push('--all');
2348+
}
2349+
if (!merges) {
2350+
args.push('--first-parent');
2351+
}
2352+
if (ordering) {
2353+
args.push(`--${ordering}-order`);
2354+
}
2355+
if (options?.authors?.length) {
2356+
args.push(
2357+
'--use-mailmap',
2358+
'--author',
2359+
...options.authors.map(a => `--author=^${a.name} <${a.email}>$`),
2360+
);
2361+
}
2362+
2363+
let hasMoreOverride;
2364+
2365+
if (options?.since) {
2366+
hasMoreOverride = true;
2367+
args.push(`--since="${options.since}"`);
2368+
}
2369+
if (options?.until) {
2370+
hasMoreOverride = true;
2371+
args.push(`--until="${options.until}"`);
2372+
}
2373+
if (options?.extraArgs?.length) {
2374+
if (
2375+
options.extraArgs.some(
2376+
arg => arg.startsWith('-n') || arg.startsWith('--until=') || arg.startsWith('--since='),
2377+
)
2378+
) {
2379+
hasMoreOverride = true;
2380+
}
2381+
args.push(...options.extraArgs);
2382+
}
2383+
2384+
if (limit) {
2385+
hasMoreOverride = undefined;
2386+
args.push(`-n${limit + 1}`);
2387+
}
2388+
2389+
const data = await this.git.log2(repoPath, options?.ref, ...args);
2390+
22662391
// const parser = GitLogParser.defaultParser;
22672392

2268-
const data = await this.git.log(repoPath, options?.ref, {
2269-
...options,
2270-
// args: parser.arguments,
2271-
limit: limit,
2272-
merges: options?.merges == null ? true : options.merges,
2273-
ordering: options?.ordering ?? configuration.get('advanced.commitOrdering'),
2274-
similarityThreshold: configuration.get('advanced.similarityThreshold'),
2275-
});
2393+
// const data = await this.git.log2(repoPath, options?.ref, {
2394+
// ...options,
2395+
// // args: parser.arguments,
2396+
// limit: limit,
2397+
// merges: options?.merges == null ? true : options.merges,
2398+
// ordering: options?.ordering ?? configuration.get('advanced.commitOrdering'),
2399+
// similarityThreshold: configuration.get('advanced.similarityThreshold'),
2400+
// });
22762401

22772402
// const commits = [];
22782403
// const entries = parser.parse(data);
@@ -2311,12 +2436,18 @@ export class LocalGitProvider implements GitProvider, Disposable {
23112436
limit,
23122437
false,
23132438
undefined,
2439+
hasMoreOverride,
23142440
);
23152441

23162442
if (log != null) {
23172443
log.query = (limit: number | undefined) => this.getLog(repoPath, { ...options, limit: limit });
23182444
if (log.hasMore) {
2319-
log.more = this.getLogMoreFn(log, options);
2445+
let opts;
2446+
if (options != null) {
2447+
let extraArgs;
2448+
({ extraArgs, ...opts } = options);
2449+
}
2450+
log.more = this.getLogMoreFn(log, opts);
23202451
}
23212452
}
23222453

@@ -2414,12 +2545,19 @@ export class LocalGitProvider implements GitProvider, Disposable {
24142545
...options,
24152546
limit: moreUntil == null ? moreLimit : 0,
24162547
...(timestamp
2417-
? { until: timestamp }
2548+
? {
2549+
until: timestamp,
2550+
extraArgs: ['--boundary'],
2551+
}
24182552
: { ref: moreUntil == null ? `${ref}^` : `${moreUntil}^..${ref}^` }),
24192553
});
24202554
// If we can't find any more, assume we have everything
24212555
if (moreLog == null) return { ...log, hasMore: false, more: undefined };
24222556

2557+
if (timestamp != null && ref != null && !moreLog.commits.has(ref)) {
2558+
debugger;
2559+
}
2560+
24232561
const commits = new Map([...log.commits, ...moreLog.commits]);
24242562

24252563
const mergedLog: GitLog = {

src/git/models/graph.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export interface GitGraphRow extends GraphRow {
2222
export interface GitGraph {
2323
readonly repoPath: string;
2424
readonly rows: GitGraphRow[];
25+
readonly sha?: string;
2526

2627
readonly paging?: {
2728
readonly limit: number | undefined;

src/git/parsers/logParser.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ export class GitLogParser {
295295
limit: number | undefined,
296296
reverse: boolean,
297297
range: Range | undefined,
298+
hasMoreOverride?: boolean,
298299
): GitLog | undefined {
299300
if (!data) return undefined;
300301

@@ -595,7 +596,7 @@ export class GitLogParser {
595596
count: i,
596597
limit: limit,
597598
range: range,
598-
hasMore: Boolean(truncationCount && i > truncationCount && truncationCount !== 1),
599+
hasMore: hasMoreOverride ?? Boolean(truncationCount && i > truncationCount && truncationCount !== 1),
599600
};
600601
return log;
601602
}

src/plus/github/githubGitProvider.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,9 +1076,10 @@ export class GitHubGitProvider implements GitProvider, Disposable {
10761076
remote: GitRemote | undefined,
10771077
tags: GitTag[] | undefined,
10781078
options?: {
1079-
ref?: string;
1080-
mode?: 'single' | 'local' | 'all';
10811079
branch?: string;
1080+
limit?: number;
1081+
mode?: 'single' | 'local' | 'all';
1082+
ref?: string;
10821083
},
10831084
): Promise<GitGraph> {
10841085
if (log == null) {

src/plus/webviews/graph/graphWebview.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ import {
3838
UpdateSelectionCommandType,
3939
} from './protocol';
4040

41+
export interface ShowCommitInGraphCommandArgs {
42+
sha: string;
43+
}
44+
4145
export interface GraphSelectionChangeEvent {
4246
readonly selection: GitCommit[];
4347
}
@@ -74,6 +78,7 @@ export class GraphWebview extends WebviewBase<State> {
7478

7579
private _etagSubscription?: number;
7680
private _etagRepository?: number;
81+
private _selectedSha?: string;
7782
private _repositoryEventsDisposable: Disposable | undefined;
7883
private _repositoryGraph?: GitGraph;
7984

@@ -101,6 +106,15 @@ export class GraphWebview extends WebviewBase<State> {
101106
},
102107
},
103108
this.container.subscription.onDidChange(this.onSubscriptionChanged, this),
109+
registerCommand(Commands.ShowCommitInGraph, (args: ShowCommitInGraphCommandArgs) => {
110+
this._selectedSha = args.sha;
111+
if (this._panel == null) {
112+
void this.show();
113+
} else {
114+
// TODO@eamodio we should be smarter here an look for the commit in the saved data before refreshing and only send the selectedSha
115+
this.updateState();
116+
}
117+
}),
104118
);
105119

106120
this.onConfigurationChanged();
@@ -123,7 +137,6 @@ export class GraphWebview extends WebviewBase<State> {
123137
}
124138

125139
if (this.repository != null) {
126-
this.resetRepositoryState();
127140
this.updateState();
128141
}
129142
}
@@ -301,11 +314,12 @@ export class GraphWebview extends WebviewBase<State> {
301314
}
302315

303316
private async onSelectionChanged(selection: string[]) {
304-
const ref = selection[0];
317+
const sha = selection[0];
318+
this._selectedSha = sha;
305319

306320
let commits: GitCommit[] | undefined;
307-
if (ref != null) {
308-
const commit = await this.repository?.getCommit(ref);
321+
if (sha != null) {
322+
const commit = await this.repository?.getCommit(sha);
309323
if (commit != null) {
310324
commits = [commit];
311325
}
@@ -421,14 +435,16 @@ export class GraphWebview extends WebviewBase<State> {
421435
const data = await this.container.git.getCommitsForGraph(
422436
this.repository.path,
423437
this._panel!.webview.asWebviewUri,
424-
{ limit: limit },
438+
{ limit: limit, ref: this._selectedSha ?? 'HEAD' },
425439
);
426440
this._repositoryGraph = data;
441+
this._selectedSha = data.sha;
427442

428443
return {
429444
previewBanner: this.previewBanner,
430445
repositories: formatRepositories(this.container.git.openRepositories),
431446
selectedRepository: this.repository.path,
447+
selectedSha: this._selectedSha,
432448
selectedVisibility: access.visibility,
433449
subscription: access.subscription.current,
434450
allowed: access.allowed,
@@ -445,6 +461,7 @@ export class GraphWebview extends WebviewBase<State> {
445461

446462
private resetRepositoryState() {
447463
this._repositoryGraph = undefined;
464+
this._selectedSha = undefined;
448465
}
449466
}
450467

src/plus/webviews/graph/protocol.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { IpcCommandType, IpcNotificationType } from '../../../webviews/protocol'
77
export interface State {
88
repositories?: GraphRepository[];
99
selectedRepository?: string;
10+
selectedSha?: string;
1011
selectedVisibility?: RepositoryVisibility;
1112
subscription?: Subscription;
1213
allowed?: boolean;

0 commit comments

Comments
 (0)