瀏覽代碼

Reworks node paging for much better performance

main
Eric Amodio 5 年之前
父節點
當前提交
816fc72e2d
共有 40 個文件被更改,包括 760 次插入431 次删除
  1. +1
    -1
      src/commands/copyMessageToClipboard.ts
  2. +1
    -1
      src/commands/copyShaToClipboard.ts
  3. +7
    -7
      src/commands/diffWithRevision.ts
  4. +1
    -1
      src/commands/git/search.ts
  5. +7
    -7
      src/commands/openFileRevision.ts
  6. +2
    -2
      src/commands/showQuickBranchHistory.ts
  7. +1
    -1
      src/commands/showQuickCommitDetails.ts
  8. +9
    -7
      src/commands/showQuickFileHistory.ts
  9. +22
    -16
      src/git/git.ts
  10. +182
    -40
      src/git/gitService.ts
  11. +6
    -4
      src/git/models/log.ts
  12. +4
    -2
      src/git/models/reflog.ts
  13. +5
    -5
      src/git/parsers/logParser.ts
  14. +14
    -8
      src/git/parsers/reflogParser.ts
  15. +9
    -9
      src/quickpicks/branchHistoryQuickPick.ts
  16. +2
    -2
      src/quickpicks/commitFileQuickPick.ts
  17. +1
    -1
      src/quickpicks/commitQuickPick.ts
  18. +3
    -3
      src/quickpicks/fileHistoryQuickPick.ts
  19. +2
    -2
      src/quickpicks/repoStatusQuickPick.ts
  20. +35
    -12
      src/views/nodes/branchNode.ts
  21. +54
    -20
      src/views/nodes/branchTrackingStatusNode.ts
  22. +1
    -1
      src/views/nodes/commitFileNode.ts
  23. +0
    -5
      src/views/nodes/common.ts
  24. +19
    -9
      src/views/nodes/compareBranchNode.ts
  25. +19
    -9
      src/views/nodes/compareResultsNode.ts
  26. +40
    -12
      src/views/nodes/contributorNode.ts
  27. +40
    -12
      src/views/nodes/fileHistoryNode.ts
  28. +41
    -13
      src/views/nodes/lineHistoryNode.ts
  29. +35
    -12
      src/views/nodes/reflogNode.ts
  30. +45
    -18
      src/views/nodes/reflogRecordNode.ts
  31. +35
    -32
      src/views/nodes/resultsCommitsNode.ts
  32. +1
    -1
      src/views/nodes/searchResultsCommitsNode.ts
  33. +1
    -1
      src/views/nodes/stashNode.ts
  34. +1
    -1
      src/views/nodes/statusFilesNode.ts
  35. +40
    -12
      src/views/nodes/tagNode.ts
  36. +3
    -5
      src/views/nodes/viewNode.ts
  37. +5
    -2
      src/views/repositoriesView.ts
  38. +47
    -25
      src/views/searchView.ts
  39. +19
    -104
      src/views/viewBase.ts
  40. +0
    -6
      src/views/viewCommands.ts

+ 1
- 1
src/commands/copyMessageToClipboard.ts 查看文件

@ -46,7 +46,7 @@ export class CopyMessageToClipboardCommand extends ActiveEditorCommand {
repoPath = await Container.git.getActiveRepoPath(editor);
if (!repoPath) return undefined;
const log = await Container.git.getLog(repoPath, { maxCount: 1 });
const log = await Container.git.getLog(repoPath, { limit: 1 });
if (!log) return undefined;
args.message = Iterables.first(log.commits.values()).message;

+ 1
- 1
src/commands/copyShaToClipboard.ts 查看文件

@ -44,7 +44,7 @@ export class CopyShaToClipboardCommand extends ActiveEditorCommand {
const repoPath = await Container.git.getActiveRepoPath(editor);
if (!repoPath) return undefined;
const log = await Container.git.getLog(repoPath, { maxCount: 1 });
const log = await Container.git.getLog(repoPath, { limit: 1 });
if (!log) return undefined;
args.sha = Iterables.first(log.commits.values()).sha;

+ 7
- 7
src/commands/diffWithRevision.ts 查看文件

@ -12,7 +12,7 @@ import { DiffWithCommandArgs } from './diffWith';
export interface DiffWithRevisionCommandArgs {
reference?: GitBranch | GitTag | GitReference;
maxCount?: number;
limit?: number;
line?: number;
showOptions?: TextDocumentShowOptions;
@ -45,7 +45,7 @@ export class DiffWithRevisionCommand extends ActiveEditorCommand {
const progressCancellation = FileHistoryQuickPick.showProgress(placeHolder);
try {
const log = await Container.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, {
maxCount: args.maxCount,
limit: args.limit,
ref: (args.reference && args.reference.ref) || gitUri.sha
});
if (log === undefined) {
@ -60,12 +60,12 @@ export class DiffWithRevisionCommand extends ActiveEditorCommand {
let previousPageCommand: CommandQuickPickItem | undefined = undefined;
let commandArgs: DiffWithRevisionCommandArgs;
if (log.truncated) {
if (log.hasMore) {
commandArgs = { ...args };
const npc = new CommandQuickPickItem(
{
label: '$(arrow-right) Show Next Commits',
description: `shows ${log.maxCount} newer commits`
description: `shows ${log.limit} newer commits`
},
Commands.DiffWithRevision,
[uri, commandArgs]
@ -77,7 +77,7 @@ export class DiffWithRevisionCommand extends ActiveEditorCommand {
previousPageCommand = new CommandQuickPickItem(
{
label: '$(arrow-left) Show Previous Commits',
description: `shows ${log.maxCount} older commits`
description: `shows ${log.limit} older commits`
},
Commands.DiffWithRevision,
[new GitUri(uri, last), commandArgs]
@ -106,14 +106,14 @@ export class DiffWithRevisionCommand extends ActiveEditorCommand {
[uri, commandArgs]
);
commandArgs = { ...args, maxCount: 0 };
commandArgs = { ...args, limit: 0 };
const pick = await FileHistoryQuickPick.show(log, gitUri, placeHolder, {
pickerOnly: true,
progressCancellation: progressCancellation,
currentCommand: currentCommand,
nextPageCommand: args.nextPageCommand,
previousPageCommand: previousPageCommand,
showAllCommand: log.truncated
showAllCommand: log.hasMore
? new CommandQuickPickItem(
{
label: '$(sync) Show All Commits',

+ 1
- 1
src/commands/git/search.ts 查看文件

@ -344,7 +344,7 @@ export class SearchGitCommand extends QuickCommandBase {
results === undefined
? `No results for ${state.pattern}`
: `${Strings.pluralize('result', results.count, {
number: results.truncated ? `${results.count}+` : undefined
number: results.hasMore ? `${results.count}+` : undefined
})} for ${state.pattern}`,
matchOnDescription: true,
matchOnDetail: true,

+ 7
- 7
src/commands/openFileRevision.ts 查看文件

@ -13,7 +13,7 @@ import { ActiveEditorCommand, command, Commands, getCommandUri, openEditor } fro
export interface OpenFileRevisionCommandArgs {
reference?: GitBranch | GitTag | GitReference;
uri?: Uri;
maxCount?: number;
limit?: number;
line?: number;
showOptions?: TextDocumentShowOptions;
@ -76,7 +76,7 @@ export class OpenFileRevisionCommand extends ActiveEditorCommand {
progressCancellation = FileHistoryQuickPick.showProgress(placeHolder);
const log = await Container.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, {
maxCount: args.maxCount,
limit: args.limit,
ref: (args.reference && args.reference.ref) || gitUri.sha
});
if (log === undefined) {
@ -90,12 +90,12 @@ export class OpenFileRevisionCommand extends ActiveEditorCommand {
let previousPageCommand: CommandQuickPickItem | undefined = undefined;
if (log.truncated) {
if (log.hasMore) {
commandArgs = { ...args };
const npc = new CommandQuickPickItem(
{
label: '$(arrow-right) Show Next Commits',
description: `shows ${log.maxCount} newer commits`
description: `shows ${log.limit} newer commits`
},
Commands.OpenFileRevision,
[uri, commandArgs]
@ -107,7 +107,7 @@ export class OpenFileRevisionCommand extends ActiveEditorCommand {
previousPageCommand = new CommandQuickPickItem(
{
label: '$(arrow-left) Show Previous Commits',
description: `shows ${log.maxCount} older commits`
description: `shows ${log.limit} older commits`
},
Commands.OpenFileRevision,
[new GitUri(uri, last), commandArgs]
@ -136,14 +136,14 @@ export class OpenFileRevisionCommand extends ActiveEditorCommand {
[uri, commandArgs]
);
commandArgs = { ...args, maxCount: 0 };
commandArgs = { ...args, limit: 0 };
const pick = await FileHistoryQuickPick.show(log, gitUri, placeHolder, {
pickerOnly: true,
progressCancellation: progressCancellation,
currentCommand: currentCommand,
nextPageCommand: args.nextPageCommand,
previousPageCommand: previousPageCommand,
showAllCommand: log.truncated
showAllCommand: log.hasMore
? new CommandQuickPickItem(
{
label: '$(sync) Show All Commits',

+ 2
- 2
src/commands/showQuickBranchHistory.ts 查看文件

@ -17,7 +17,7 @@ import { ShowQuickCommitDetailsCommandArgs } from './showQuickCommitDetails';
export interface ShowQuickBranchHistoryCommandArgs {
branch?: string;
log?: GitLog;
maxCount?: number;
limit?: number;
repoPath?: string;
goBackCommand?: CommandQuickPickItem;
@ -81,7 +81,7 @@ export class ShowQuickBranchHistoryCommand extends ActiveEditorCachedCommand {
if (args.log === undefined) {
args.log = await Container.git.getLog(repoPath, {
maxCount: args.maxCount,
limit: args.limit,
ref: (gitUri && gitUri.sha) || args.branch
});
if (args.log === undefined) return window.showWarningMessage('Unable to show branch history');

+ 1
- 1
src/commands/showQuickCommitDetails.ts 查看文件

@ -106,7 +106,7 @@ export class ShowQuickCommitDetailsCommand extends ActiveEditorCachedCommand {
}
if (args.repoLog === undefined) {
const log = await Container.git.getLog(repoPath!, { maxCount: 2, ref: args.sha });
const log = await Container.git.getLog(repoPath!, { limit: 2, ref: args.sha });
if (log === undefined) {
return Messages.showCommitNotFoundWarningMessage('Unable to show commit details');
}

+ 9
- 7
src/commands/showQuickFileHistory.ts 查看文件

@ -18,7 +18,7 @@ import { ShowQuickCommitFileDetailsCommandArgs } from './showQuickCommitFileDeta
export interface ShowQuickFileHistoryCommandArgs {
reference?: GitBranch | GitTag | GitReference;
log?: GitLog;
maxCount?: number;
limit?: number;
range?: Range;
showInView?: boolean;
@ -64,7 +64,7 @@ export class ShowQuickFileHistoryCommand extends ActiveEditorCachedCommand {
try {
if (args.log === undefined) {
args.log = await Container.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, {
maxCount: args.maxCount,
limit: args.limit,
range: args.range,
ref: (args.reference && args.reference.ref) || gitUri.sha
});
@ -82,13 +82,13 @@ export class ShowQuickFileHistoryCommand extends ActiveEditorCachedCommand {
let previousPageCommand: CommandQuickPickItem | undefined = undefined;
if (args.log.truncated) {
if (args.log.hasMore) {
let commandArgs: ShowQuickFileHistoryCommandArgs;
commandArgs = { ...args, log: undefined };
const npc = new CommandQuickPickItem(
{
label: '$(arrow-right) Show Next Commits',
description: `shows ${args.log.maxCount} newer commits`
description: `shows ${args.log.limit} newer commits`
},
Commands.ShowQuickFileHistory,
[gitUri, commandArgs]
@ -100,7 +100,7 @@ export class ShowQuickFileHistoryCommand extends ActiveEditorCachedCommand {
previousPageCommand = new CommandQuickPickItem(
{
label: '$(arrow-left) Show Previous Commits',
description: `shows ${args.log.maxCount} older commits`
description: `shows ${args.log.limit} older commits`
},
Commands.ShowQuickFileHistory,
[new GitUri(uri, last), commandArgs]
@ -129,6 +129,8 @@ export class ShowQuickFileHistoryCommand extends ActiveEditorCachedCommand {
[uri, args]
);
const showAllCommandArgs: ShowQuickFileHistoryCommandArgs = { ...args, log: undefined, limit: 0 };
const pick = await FileHistoryQuickPick.show(args.log, gitUri, placeHolder, {
progressCancellation: progressCancellation,
currentCommand: currentCommand,
@ -136,14 +138,14 @@ export class ShowQuickFileHistoryCommand extends ActiveEditorCachedCommand {
nextPageCommand: args.nextPageCommand,
previousPageCommand: previousPageCommand,
showAllCommand:
args.log !== undefined && args.log.truncated
args.log !== undefined && args.log.hasMore
? new CommandQuickPickItem(
{
label: '$(sync) Show All Commits',
description: 'this may take a while'
},
Commands.ShowQuickFileHistory,
[uri, { ...args, log: undefined, maxCount: 0 }]
[uri, showAllCommandArgs]
)
: undefined,
showInViewCommand:

+ 22
- 16
src/git/git.ts 查看文件

@ -693,11 +693,11 @@ export namespace Git {
ref: string | undefined,
{
authors,
maxCount,
limit,
merges,
reverse,
similarityThreshold
}: { authors?: string[]; maxCount?: number; merges?: boolean; reverse?: boolean; similarityThreshold?: number }
}: { authors?: string[]; limit?: number; merges?: boolean; reverse?: boolean; similarityThreshold?: number }
) {
const params = [
'log',
@ -706,8 +706,8 @@ export namespace Git {
'--full-history',
`-M${similarityThreshold == null ? '' : `${similarityThreshold}%`}`
];
if (maxCount && !reverse) {
params.push(`-n${maxCount}`);
if (limit && !reverse) {
params.push(`-n${limit}`);
}
if (merges) {
@ -740,7 +740,7 @@ export namespace Git {
ref: string | undefined,
{
filters,
maxCount,
limit,
firstParent = false,
renames = true,
reverse = false,
@ -749,7 +749,7 @@ export namespace Git {
endLine
}: {
filters?: GitLogDiffFilter[];
maxCount?: number;
limit?: number;
firstParent?: boolean;
renames?: boolean;
reverse?: boolean;
@ -762,8 +762,8 @@ export namespace Git {
const params = ['log', `--format=${simple ? GitLogParser.simpleFormat : GitLogParser.defaultFormat}`];
if (maxCount && !reverse) {
params.push(`-n${maxCount}`);
if (limit && !reverse) {
params.push(`-n${limit}`);
}
params.push(renames ? '--follow' : '-m');
@ -848,7 +848,7 @@ export namespace Git {
export function log__search(
repoPath: string,
search: string[] = emptyArray,
{ maxCount, useShow }: { maxCount?: number; useShow?: boolean } = {}
{ limit, skip, useShow }: { limit?: number; skip?: number; useShow?: boolean } = {}
) {
const params = [
useShow ? 'show' : 'log',
@ -856,8 +856,11 @@ export namespace Git {
`--format=${GitLogParser.defaultFormat}`,
'--use-mailmap'
];
if (maxCount && !useShow) {
params.push(`-n${maxCount}`);
if (limit && !useShow) {
params.push(`-n${limit}`);
}
if (skip && !useShow) {
params.push(`--skip=${skip}`);
}
return git<string>({ cwd: repoPath }, ...params, ...search);
@ -916,18 +919,21 @@ export namespace Git {
export function reflog(
repoPath: string,
{ all, branch, since }: { all?: boolean; branch?: string; since?: string } = {}
{ all, branch, limit, skip }: { all?: boolean; branch?: string; limit?: number; skip?: number } = {}
): Promise<string> {
const params = ['log', '-g', `--format=${GitReflogParser.defaultFormat}`, '--date=iso8601'];
const params = ['log', '--walk-reflogs', `--format=${GitReflogParser.defaultFormat}`, '--date=iso8601'];
if (all) {
params.push('--all');
}
if (limit) {
params.push(`-n${limit}`);
}
if (skip) {
params.push(`--skip=${skip}`);
}
if (branch) {
params.push(branch);
}
if (since) {
params.push(`--since=${since}`);
}
return git<string>({ cwd: repoPath }, ...params, '--');
}

+ 182
- 40
src/git/gitService.ts 查看文件

@ -1196,7 +1196,7 @@ export class GitService implements Disposable {
@log()
async getCommit(repoPath: string, ref: string): Promise<GitLogCommit | undefined> {
const log = await this.getLog(repoPath, { maxCount: 2, ref: ref });
const log = await this.getLog(repoPath, { limit: 2, ref: ref });
if (log === undefined) return undefined;
return log.commits.get(ref) || Iterables.first(log.commits.values());
@ -1225,7 +1225,7 @@ export class GitService implements Disposable {
options: { ref?: string; firstIfNotFound?: boolean; reverse?: boolean } = {}
): Promise<GitLogCommit | undefined> {
const log = await this.getLogForFile(repoPath, fileName, {
maxCount: 2,
limit: 2,
ref: options.ref,
reverse: options.reverse
});
@ -1463,14 +1463,14 @@ export class GitService implements Disposable {
{
ref,
...options
}: { authors?: string[]; maxCount?: number; merges?: boolean; ref?: string; reverse?: boolean } = {}
}: { authors?: string[]; limit?: number; merges?: boolean; ref?: string; reverse?: boolean } = {}
): Promise<GitLog | undefined> {
const maxCount = options.maxCount == null ? Container.config.advanced.maxListItems || 0 : options.maxCount;
const limit = options.limit ?? Container.config.advanced.maxListItems ?? 0;
try {
const data = await Git.log(repoPath, ref, {
authors: options.authors,
maxCount: maxCount,
limit: limit,
merges: options.merges === undefined ? true : options.merges,
reverse: options.reverse,
similarityThreshold: Container.config.advanced.similarityThreshold
@ -1482,14 +1482,17 @@ export class GitService implements Disposable {
undefined,
ref,
await this.getCurrentUser(repoPath),
maxCount,
limit,
options.reverse!,
undefined
);
if (log !== undefined) {
const opts = { ...options, ref: ref };
log.query = (maxCount: number | undefined) => this.getLog(repoPath, { ...opts, maxCount: maxCount });
log.query = (limit: number | undefined) => this.getLog(repoPath, { ...opts, limit: limit });
if (log.hasMore) {
log.more = this.getLogMoreFn(log, opts);
}
}
return log;
@ -1498,17 +1501,55 @@ export class GitService implements Disposable {
}
}
private getLogMoreFn(log: GitLog, options: { authors?: string[]; limit?: number; merges?: boolean; ref?: string; reverse?: boolean }): (limit: number | { until: string } | undefined) => Promise<GitLog> {
return async (limit: number | { until: string } | undefined) => {
const moreUntil = limit != null && typeof limit === 'object' ? limit.until : undefined;
let moreLimit = typeof limit === 'number' ? limit : undefined;
if (moreUntil && Iterables.some(log.commits.values(), c => c.ref === moreUntil)) {
return log;
}
moreLimit = moreLimit ?? Container.config.advanced.maxSearchItems ?? 0;
const ref = Iterables.last(log.commits.values())?.ref;
const moreLog = await this.getLog(log.repoPath, {
...options,
limit: moreUntil == null ? moreLimit : 0,
ref: moreUntil == null ? `${ref}^` : `${moreUntil}^..${ref}^`
});
if (moreLog === undefined) {
// If we can't find any more, assume we have everything
return { ...log, hasMore: false };
}
const mergedLog: GitLog = {
repoPath: log.repoPath,
authors: new Map([...log.authors, ...moreLog.authors]),
commits: new Map([...log.commits, ...moreLog.commits]),
sha: log.sha,
range: undefined,
count: log.count + moreLog.count,
limit: moreUntil == null ? (log.limit ?? 0) + moreLimit : undefined,
hasMore: moreUntil == null ? moreLog.hasMore : true,
query: (limit: number | undefined) => this.getLog(log.repoPath, { ...options, limit: limit })
};
mergedLog.more = this.getLogMoreFn(mergedLog, options);
return mergedLog;
};
}
@log()
async getLogForSearch(
repoPath: string,
search: SearchPattern,
options: { maxCount?: number } = {}
options: { limit?: number; skip?: number } = {}
): Promise<GitLog | undefined> {
search = { matchAll: false, matchCase: false, matchRegex: true, ...search };
try {
const maxCount =
options.maxCount == null ? Container.config.advanced.maxSearchItems || 0 : options.maxCount;
const limit = options.limit ?? Container.config.advanced.maxSearchItems ?? 0;
const similarityThreshold = Container.config.advanced.similarityThreshold;
const operations = GitService.parseSearchOperations(search.pattern);
@ -1580,7 +1621,7 @@ export class GitService implements Disposable {
args.push(...files);
}
const data = await Git.log__search(repoPath, args, { maxCount: maxCount, useShow: useShow });
const data = await Git.log__search(repoPath, args, { ...options, limit: limit, useShow: useShow });
const log = GitLogParser.parse(
data,
GitCommitType.Log,
@ -1588,14 +1629,16 @@ export class GitService implements Disposable {
undefined,
undefined,
await this.getCurrentUser(repoPath),
maxCount,
limit,
false,
undefined
);
if (log !== undefined) {
log.query = (maxCount: number | undefined) =>
this.getLogForSearch(repoPath, search, { maxCount: maxCount });
log.query = (limit: number | undefined) => this.getLogForSearch(repoPath, search, { ...options, limit: limit });
if (log.hasMore) {
log.more = this.getLogForSearchMoreFn(log, search, options);
}
}
return log;
@ -1604,11 +1647,41 @@ export class GitService implements Disposable {
}
}
private getLogForSearchMoreFn(log: GitLog, search: SearchPattern, options: { limit?: number }): (limit: number | undefined) => Promise<GitLog> {
return async (limit: number | undefined) => {
limit = limit ?? Container.config.advanced.maxSearchItems ?? 0;
const moreLog = await this.getLogForSearch(log.repoPath, search, {
...options,
limit: limit,
skip: log.count
});
if (moreLog === undefined) {
// If we can't find any more, assume we have everything
return { ...log, hasMore: false };
}
const mergedLog: GitLog = {
repoPath: log.repoPath,
authors: new Map([...log.authors, ...moreLog.authors]),
commits: new Map([...log.commits, ...moreLog.commits]),
sha: log.sha,
range: log.range,
count: log.count + moreLog.count,
limit: (log.limit ?? 0) + limit,
hasMore: moreLog.hasMore,
query: (limit: number | undefined) => this.getLogForSearch(log.repoPath, search, { ...options, limit: limit })
};
mergedLog.more = this.getLogForSearchMoreFn(mergedLog, search, options);
return mergedLog;
};
}
@log()
async getLogForFile(
repoPath: string | undefined,
fileName: string,
options: { maxCount?: number; range?: Range; ref?: string; renames?: boolean; reverse?: boolean } = {}
options: { limit?: number; range?: Range; ref?: string; renames?: boolean; reverse?: boolean } = {}
): Promise<GitLog | undefined> {
if (repoPath !== undefined && repoPath === Strings.normalizePath(fileName)) {
throw new Error(`File name cannot match the repository path; fileName=${fileName}`);
@ -1627,9 +1700,9 @@ export class GitService implements Disposable {
key += `:${options.ref}`;
}
options.maxCount = options.maxCount == null ? Container.config.advanced.maxListItems || 0 : options.maxCount;
if (options.maxCount) {
key += `:n${options.maxCount}`;
options.limit = options.limit == null ? Container.config.advanced.maxListItems || 0 : options.limit;
if (options.limit) {
key += `:n${options.limit}`;
}
if (options.renames) {
@ -1649,7 +1722,7 @@ export class GitService implements Disposable {
return cachedLog.item;
}
if (options.ref !== undefined || options.maxCount !== undefined) {
if (options.ref !== undefined || options.limit !== undefined) {
// Since we are looking for partial log, see if we have the log of the whole file
const cachedLog = doc.state.get<CachedLog>(
`log${options.renames ? ':follow' : emptyStr}${options.reverse ? ':reverse' : emptyStr}`
@ -1662,7 +1735,7 @@ export class GitService implements Disposable {
Logger.debug(cc, `Cache ?: '${key}'`);
let log = await cachedLog.item;
if (log !== undefined && !log.truncated && log.commits.has(options.ref)) {
if (log !== undefined && !log.hasMore && log.commits.has(options.ref)) {
Logger.debug(cc, `Cache hit: '${key}'`);
// Create a copy of the log starting at the requested commit
@ -1679,7 +1752,7 @@ export class GitService implements Disposable {
}
i++;
if (options.maxCount !== undefined && i > options.maxCount) {
if (options.limit !== undefined && i > options.limit) {
return undefined;
}
@ -1692,12 +1765,12 @@ export class GitService implements Disposable {
const opts = { ...options };
log = {
...log,
maxCount: options.maxCount,
limit: options.limit,
count: commits.size,
commits: commits,
authors: authors,
query: (maxCount: number | undefined) =>
this.getLogForFile(repoPath, fileName, { ...opts, maxCount: maxCount })
query: (limit: number | undefined) =>
this.getLogForFile(repoPath, fileName, { ...opts, limit: limit })
};
return log;
@ -1734,7 +1807,7 @@ export class GitService implements Disposable {
ref,
range,
...options
}: { maxCount?: number; range?: Range; ref?: string; renames?: boolean; reverse?: boolean },
}: { limit?: number; range?: Range; ref?: string; renames?: boolean; reverse?: boolean },
document: TrackedDocument<GitDocumentState>,
key: string,
cc: LogCorrelationContext | undefined
@ -1763,15 +1836,17 @@ export class GitService implements Disposable {
file,
ref,
await this.getCurrentUser(root),
options.maxCount,
options.limit,
options.reverse!,
range
);
if (log !== undefined) {
const opts = { ...options, ref: ref, range: range };
log.query = (maxCount: number | undefined) =>
this.getLogForFile(repoPath, fileName, { ...opts, maxCount: maxCount });
log.query = (limit: number | undefined) => this.getLogForFile(repoPath, fileName, { ...opts, limit: limit });
if (log.hasMore) {
log.more = this.getLogForFileMoreFn(log, fileName, opts);
}
}
return log;
@ -1794,6 +1869,45 @@ export class GitService implements Disposable {
}
}
private getLogForFileMoreFn(log: GitLog, fileName: string, options: { limit?: number; range?: Range; ref?: string; renames?: boolean; reverse?: boolean }): (limit: number | { until: string } | undefined) => Promise<GitLog> {
return async (limit: number | { until: string } | undefined) => {
const moreUntil = limit != null && typeof limit === 'object' ? limit.until : undefined;
let moreLimit = typeof limit === 'number' ? limit : undefined;
if (moreUntil && Iterables.some(log.commits.values(), c => c.ref === moreUntil)) {
return log;
}
moreLimit = moreLimit ?? Container.config.advanced.maxSearchItems ?? 0;
const ref = Iterables.last(log.commits.values())?.ref;
const moreLog = await this.getLogForFile(log.repoPath, fileName, {
...options,
limit: moreUntil == null ? moreLimit : 0,
ref: moreUntil == null ? `${ref}^` : `${moreUntil}^..${ref}^`
});
if (moreLog === undefined) {
// If we can't find any more, assume we have everything
return { ...log, hasMore: false };
}
const mergedLog: GitLog = {
repoPath: log.repoPath,
authors: new Map([...log.authors, ...moreLog.authors]),
commits: new Map([...log.commits, ...moreLog.commits]),
sha: log.sha,
range: log.range,
count: log.count + moreLog.count,
limit: moreUntil == null ? (log.limit ?? 0) + moreLimit : undefined,
hasMore: moreUntil == null ? moreLog.hasMore : true,
query: (limit: number | undefined) => this.getLogForFile(log.repoPath, fileName, { ...options, limit: limit })
};
mergedLog.more = this.getLogForFileMoreFn(mergedLog, fileName, options);
return mergedLog;
};
}
@log()
async hasRemotes(repoPath: string | undefined): Promise<boolean> {
if (repoPath === undefined) return false;
@ -1897,7 +2011,7 @@ export class GitService implements Disposable {
const fileName = GitUri.relativeTo(uri, repoPath);
let data = await Git.log__file(repoPath, fileName, ref, {
filters: filters,
maxCount: skip + 1,
limit: skip + 1,
// startLine: editorLine !== undefined ? editorLine + 1 : undefined,
reverse: true,
simple: true
@ -1909,7 +2023,7 @@ export class GitService implements Disposable {
if (status === 'D') {
data = await Git.log__file(repoPath, '.', nextRef, {
filters: ['R'],
maxCount: 1,
limit: 1,
// startLine: editorLine !== undefined ? editorLine + 1 : undefined
simple: true
});
@ -2147,7 +2261,7 @@ export class GitService implements Disposable {
let data;
try {
data = await Git.log__file(repoPath, fileName, ref, {
maxCount: skip + 2,
limit: skip + 2,
firstParent: firstParent,
simple: true,
startLine: editorLine !== undefined ? editorLine + 1 : undefined
@ -2184,20 +2298,20 @@ export class GitService implements Disposable {
@log()
async getIncomingActivity(
repoPath: string,
{ maxCount, ...options }: { all?: boolean; branch?: string; maxCount?: number; since?: string } = {}
{ limit, ...options }: { all?: boolean; branch?: string; limit?: number; skip?: number } = {}
): Promise<GitReflog | undefined> {
const cc = Logger.getCorrelationContext();
limit = limit ?? Container.config.advanced.maxListItems ?? 0;
try {
const data = await Git.reflog(repoPath, options);
// Pass a much larger limit to reflog, because we aggregate the data and we won't know how many lines we'll need
const data = await Git.reflog(repoPath, { ...options, limit: limit * 100 });
if (data === undefined) return undefined;
const reflog = GitReflogParser.parse(
data,
repoPath,
reflogCommands,
maxCount == null ? Container.config.advanced.maxListItems || 0 : maxCount
);
const reflog = GitReflogParser.parse( data, repoPath, reflogCommands, limit, limit * 100);
if (reflog?.hasMore) {
reflog.more = this.getReflogMoreFn(reflog, options);
}
return reflog;
} catch (ex) {
@ -2206,6 +2320,34 @@ export class GitService implements Disposable {
}
}
private getReflogMoreFn(reflog: GitReflog, options: { all?: boolean; branch?: string; limit?: number; skip?: number }): (limit: number) => Promise<GitReflog> {
return async (limit: number | undefined) => {
limit = limit ?? Container.config.advanced.maxSearchItems ?? 0;
const moreLog = await this.getIncomingActivity(reflog.repoPath, {
...options,
limit: limit,
skip: reflog.total
});
if (moreLog === undefined) {
// If we can't find any more, assume we have everything
return { ...reflog, hasMore: false };
}
const mergedLog: GitReflog = {
repoPath: reflog.repoPath,
records: [...reflog.records, ...moreLog.records],
count: reflog.count + moreLog.count,
total: reflog.total + moreLog.total,
limit: (reflog.limit ?? 0) + limit,
hasMore: moreLog.hasMore,
};
mergedLog.more = this.getReflogMoreFn(mergedLog, options);
return mergedLog;
};
}
@log()
async getRemotes(
repoPath: string | undefined,
@ -2574,7 +2716,7 @@ export class GitService implements Disposable {
// Now check if that commit had any renames
data = await Git.log__file(repoPath, '.', ref, {
filters: ['R'],
maxCount: 1,
limit: 1,
simple: true
});
if (data == null || data.length === 0) {

+ 6
- 4
src/git/models/log.ts 查看文件

@ -9,10 +9,12 @@ export interface GitLog {
readonly commits: Map<string, GitLogCommit>;
readonly sha: string | undefined;
readonly count: number;
readonly maxCount: number | undefined;
readonly range: Range | undefined;
readonly truncated: boolean;
query?(maxCount: number | undefined): Promise<GitLog | undefined>;
readonly count: number;
readonly limit: number | undefined;
readonly hasMore: boolean;
query?(limit: number | undefined): Promise<GitLog | undefined>;
more?(limit: number | { until?: string } | undefined): Promise<GitLog | undefined>;
}

+ 4
- 2
src/git/models/reflog.ts 查看文件

@ -8,8 +8,10 @@ export interface GitReflog {
readonly records: GitReflogRecord[];
readonly count: number;
readonly maxCount: number | undefined;
readonly truncated: boolean;
readonly total: number;
readonly limit: number | undefined;
readonly hasMore: boolean;
more?(limit: number | undefined): Promise<GitReflog | undefined>;
}
export class GitReflogRecord {

+ 5
- 5
src/git/parsers/logParser.ts 查看文件

@ -76,7 +76,7 @@ export class GitLogParser {
fileName: string | undefined,
sha: string | undefined,
currentUser: { name?: string; email?: string } | undefined,
maxCount: number | undefined,
limit: number | undefined,
reverse: boolean,
range: Range | undefined
): GitLog | undefined {
@ -103,7 +103,7 @@ export class GitLogParser {
const authors: Map<string, GitAuthor> = new Map();
const commits: Map<string, GitLogCommit> = new Map();
let truncationCount = maxCount;
let truncationCount = limit;
let match;
let renamedFileName;
@ -116,7 +116,7 @@ export class GitLogParser {
line = next.value;
// Since log --reverse doesn't properly honor a max count -- enforce it here
if (reverse && maxCount && i >= maxCount) break;
if (reverse && limit && i >= limit) break;
// <1-char token> data
// e.g. <r> bd1452a2dc
@ -356,9 +356,9 @@ export class GitLogParser {
commits: commits,
sha: sha,
count: i,
maxCount: maxCount,
limit: limit,
range: range,
truncated: Boolean(truncationCount && i >= truncationCount && truncationCount !== 1)
hasMore: Boolean(truncationCount && i >= truncationCount && truncationCount !== 1)
};
return log;
}

+ 14
- 8
src/git/parsers/reflogParser.ts 查看文件

@ -19,7 +19,13 @@ export class GitReflogParser {
].join('');
@debug({ args: false })
static parse(data: string, repoPath: string, commands: string[], maxCount: number): GitReflog | undefined {
static parse(
data: string,
repoPath: string,
commands: string[],
limit: number,
totalLimit: number
): GitReflog | undefined {
if (!data) return undefined;
const records: GitReflogRecord[] = [];
@ -36,9 +42,9 @@ export class GitReflogParser {
let headSha;
let count = 0;
let total = 0;
let recordDate;
let record: GitReflogRecord | undefined;
let truncated = false;
let match;
do {
@ -47,6 +53,8 @@ export class GitReflogParser {
[, sha, selector, date, command, commandArgs, details] = match;
total++;
if (record !== undefined) {
// If the next record has the same sha as the previous, use it if it is not pointing to just HEAD and the previous is
if (
@ -73,10 +81,7 @@ export class GitReflogParser {
recordDate = undefined;
count++;
if (maxCount !== 0 && count >= maxCount) {
truncated = true;
break;
}
if (limit !== 0 && count >= limit) break;
}
}
@ -114,8 +119,9 @@ export class GitReflogParser {
repoPath: repoPath,
records: records,
count: count,
maxCount: maxCount,
truncated: truncated
total: total,
limit: limit,
hasMore: (limit !== 0 && count >= limit) || (totalLimit !== 0 && total >= totalLimit)
};
}
}

+ 9
- 9
src/quickpicks/branchHistoryQuickPick.ts 查看文件

@ -37,7 +37,7 @@ export class BranchHistoryQuickPick {
const currentCommandArgs: ShowQuickBranchHistoryCommandArgs = {
branch: branch,
log: log,
maxCount: log.maxCount,
limit: log.limit,
goBackCommand: goBackCommand
};
const currentCommand = new CommandQuickPickItem(
@ -67,11 +67,11 @@ export class BranchHistoryQuickPick {
let previousPageCommand: CommandQuickPickItem | undefined = undefined;
if (log.truncated || log.sha) {
if (log.truncated) {
if (log.hasMore || log.sha) {
if (log.hasMore) {
const commandArgs: ShowQuickBranchHistoryCommandArgs = {
branch: branch,
maxCount: 0,
limit: 0,
goBackCommand: goBackCommand
};
items.splice(
@ -92,16 +92,16 @@ export class BranchHistoryQuickPick {
items.splice(0, 0, nextPageCommand);
}
if (log.truncated) {
if (log.hasMore) {
const commandArgs: ShowQuickBranchHistoryCommandArgs = {
branch: branch,
maxCount: log.maxCount,
limit: log.limit,
nextPageCommand: nextPageCommand
};
const npc = new CommandQuickPickItem(
{
label: '$(arrow-right) Show Next Commits',
description: `shows ${log.maxCount} newer commits`
description: `shows ${log.limit} newer commits`
},
Commands.ShowQuickBranchHistory,
[uri, commandArgs]
@ -111,14 +111,14 @@ export class BranchHistoryQuickPick {
if (last != null) {
const commandArgs: ShowQuickBranchHistoryCommandArgs = {
branch: branch,
maxCount: log.maxCount,
limit: log.limit,
goBackCommand: goBackCommand,
nextPageCommand: npc
};
previousPageCommand = new CommandQuickPickItem(
{
label: '$(arrow-left) Show Previous Commits',
description: `shows ${log.maxCount} older commits`
description: `shows ${log.limit} older commits`
},
Commands.ShowQuickBranchHistory,
[new GitUri(uri ? uri : last.uri, last), commandArgs]

+ 2
- 2
src/quickpicks/commitFileQuickPick.ts 查看文件

@ -334,7 +334,7 @@ export class CommitFileQuickPick {
const previousCommandArgs: ShowQuickCommitFileDetailsCommandArgs = {
// If we have the full file history, reuse it
fileLog:
fileLog !== undefined && !fileLog.truncated && fileLog.sha === undefined ? fileLog : undefined,
fileLog !== undefined && !fileLog.hasMore && fileLog.sha === undefined ? fileLog : undefined,
sha: previousUri.sha,
goBackCommand: goBackCommand
};
@ -351,7 +351,7 @@ export class CommitFileQuickPick {
const nextCommandArgs: ShowQuickCommitFileDetailsCommandArgs = {
// If we have the full file history, reuse it
fileLog:
fileLog !== undefined && !fileLog.truncated && fileLog.sha === undefined ? fileLog : undefined,
fileLog !== undefined && !fileLog.hasMore && fileLog.sha === undefined ? fileLog : undefined,
sha: nextUri.sha,
goBackCommand: goBackCommand
};

+ 1
- 1
src/quickpicks/commitQuickPick.ts 查看文件

@ -191,7 +191,7 @@ export class CommitQuickPick {
// Try to find the next commit
const nextLog = await Container.git.getLog(commit.repoPath, {
maxCount: 1,
limit: 1,
reverse: true,
ref: commit.sha
});

+ 3
- 3
src/quickpicks/fileHistoryQuickPick.ts 查看文件

@ -55,7 +55,7 @@ export class FileHistoryQuickPick {
items.splice(0, 0, options.showInViewCommand);
}
if (log.truncated || log.sha) {
if (log.hasMore || log.sha) {
if (options.showAllCommand !== undefined) {
index++;
items.splice(0, 0, options.showAllCommand);
@ -64,7 +64,7 @@ export class FileHistoryQuickPick {
if (workingUri) {
const goBackCommandArgs: ShowQuickFileHistoryCommandArgs = {
log: log,
maxCount: log.maxCount,
limit: log.limit,
range: log.range,
goBackCommand: options.goBackCommand
};
@ -115,7 +115,7 @@ export class FileHistoryQuickPick {
if (branch !== undefined) {
const commandArgs: ShowQuickFileHistoryCommandArgs = {
log: log,
maxCount: log.maxCount,
limit: log.limit,
range: log.range
};

+ 2
- 2
src/quickpicks/repoStatusQuickPick.ts 查看文件

@ -348,7 +348,7 @@ export class RepoStatusQuickPick {
if (status.upstream && status.state.ahead) {
const branchHistoryCommandArgs: ShowQuickBranchHistoryCommandArgs = {
branch: status.ref,
maxCount: 0,
limit: 0,
goBackCommand: currentCommand
};
items.splice(
@ -373,7 +373,7 @@ export class RepoStatusQuickPick {
if (status.upstream && status.state.behind) {
const branchHistoryCommandArgs: ShowQuickBranchHistoryCommandArgs = {
branch: status.ref,
maxCount: 0,
limit: 0,
goBackCommand: currentCommand
};
items.splice(

+ 35
- 12
src/views/nodes/branchNode.ts 查看文件

@ -3,7 +3,7 @@ import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { ViewBranchesLayout } from '../../configuration';
import { GlyphChars } from '../../constants';
import { Container } from '../../container';
import { BranchDateFormatting, GitBranch, GitRemoteType, GitUri } from '../../git/gitService';
import { BranchDateFormatting, GitBranch, GitLog, GitRemoteType, GitUri } from '../../git/gitService';
import { debug, gate, Iterables, log, Strings } from '../../system';
import { RepositoriesView } from '../repositoriesView';
import { BranchTrackingStatusNode } from './branchTrackingStatusNode';
@ -19,10 +19,6 @@ export class BranchNode extends ViewRefNode implements Pageabl
return `${RepositoryNode.getId(repoPath)}${this.key}(${name})${root ? ':root' : ''}`;
}
readonly supportsPaging = true;
readonly rememberLastMaxCount = true;
maxCount: number | undefined = this.view.getNodeLastMaxCount(this);
private _children: ViewNode[] | undefined;
constructor(
@ -87,10 +83,7 @@ export class BranchNode extends ViewRefNode implements Pageabl
}
}
const log = await Container.git.getLog(this.uri.repoPath!, {
maxCount: this.maxCount !== undefined ? this.maxCount : this.view.config.defaultItemLimit,
ref: this.ref
});
const log = await this.getLog();
if (log === undefined) return [new MessageNode(this.view, this, 'No commits could be found.')];
const getBranchAndTagTips = await Container.git.getBranchesAndTagsTipsFn(
@ -107,9 +100,9 @@ export class BranchNode extends ViewRefNode implements Pageabl
)
);
if (log.truncated) {
if (log.hasMore) {
children.push(
new ShowMoreNode(this.view, this, 'Commits', log.maxCount, children[children.length - 1])
new ShowMoreNode(this.view, this, 'Commits', children[children.length - 1])
);
}
@ -230,7 +223,37 @@ export class BranchNode extends ViewRefNode implements Pageabl
@gate()
@debug()
refresh() {
refresh(reset?: boolean) {
this._children = undefined;
if (reset) {
this._log = undefined;
}
}
private _log: GitLog | undefined;
private async getLog() {
if (this._log === undefined) {
this._log = await Container.git.getLog(this.uri.repoPath!, {
limit: this.view.config.defaultItemLimit,
ref: this.ref
});
}
return this._log;
}
get hasMore() {
return this._log?.hasMore ?? true;
}
async showMore(limit?: number | { until?: any }) {
let log = await this.getLog();
if (log === undefined || !log.hasMore) return;
log = await log.more?.(limit ?? this.view.config.pageItemLimit);
if (this._log === log) return;
this._log = log;
this.triggerChange(false);
}
}

+ 54
- 20
src/views/nodes/branchTrackingStatusNode.ts 查看文件

@ -1,8 +1,8 @@
'use strict';
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { Container } from '../../container';
import { GitBranch, GitTrackingState, GitUri } from '../../git/gitService';
import { Iterables, Strings } from '../../system';
import { GitBranch, GitLog, GitTrackingState, GitUri } from '../../git/gitService';
import { debug, gate, Iterables, Strings } from '../../system';
import { ViewWithFiles } from '../viewBase';
import { CommitNode } from './commitNode';
import { ShowMoreNode } from './common';
@ -23,10 +23,6 @@ export class BranchTrackingStatusNode extends ViewNode implements
return `${BranchNode.getId(repoPath, name, root)}${this.key}(${upstream}|${direction})`;
}
readonly supportsPaging = true;
readonly rememberLastMaxCount = true;
maxCount: number | undefined = this.view.getNodeLastMaxCount(this);
constructor(
view: ViewWithFiles,
parent: ViewNode,
@ -39,6 +35,14 @@ export class BranchTrackingStatusNode extends ViewNode implements
super(GitUri.fromRepoPath(status.repoPath), view, parent);
}
get ahead(): boolean {
return this.direction === 'ahead';
}
get behind(): boolean {
return this.direction === 'behind';
}
get id(): string {
return BranchTrackingStatusNode.getId(
this.status.repoPath,
@ -54,24 +58,16 @@ export class BranchTrackingStatusNode extends ViewNode implements
}
async getChildren(): Promise<ViewNode[]> {
const ahead = this.direction === 'ahead';
const range = ahead
? `${this.status.upstream}..${this.status.ref}`
: `${this.status.ref}..${this.status.upstream}`;
const log = await Container.git.getLog(this.uri.repoPath!, {
maxCount: this.maxCount !== undefined ? this.maxCount : this.view.config.defaultItemLimit,
ref: range
});
const log = await this.getLog();
if (log === undefined) return [];
let children;
if (ahead) {
if (this.ahead) {
// Since the last commit when we are looking 'ahead' can have no previous (because of the range given) -- look it up
const commits = [...log.commits.values()];
const commit = commits[commits.length - 1];
if (commit.previousSha === undefined) {
const previousLog = await Container.git.getLog(this.uri.repoPath!, { maxCount: 2, ref: commit.sha });
const previousLog = await Container.git.getLog(this.uri.repoPath!, { limit: 2, ref: commit.sha });
if (previousLog !== undefined) {
commits[commits.length - 1] = Iterables.first(previousLog.commits.values());
}
@ -94,14 +90,14 @@ export class BranchTrackingStatusNode extends ViewNode implements
];
}
if (log.truncated) {
children.push(new ShowMoreNode(this.view, this, 'Commits', log.maxCount, children[children.length - 1]));
if (log.hasMore) {
children.push(new ShowMoreNode(this.view, this, 'Commits', children[children.length - 1]));
}
return children;
}
getTreeItem(): TreeItem {
const ahead = this.direction === 'ahead';
const ahead = this.ahead;
const label = ahead
? `${Strings.pluralize('commit', this.status.state.ahead)} ahead`
: `${Strings.pluralize('commit', this.status.state.behind)} behind`;
@ -125,4 +121,42 @@ export class BranchTrackingStatusNode extends ViewNode implements
return item;
}
@gate()
@debug()
refresh(reset?: boolean) {
if (reset) {
this._log = undefined;
}
}
private _log: GitLog | undefined;
private async getLog() {
if (this._log === undefined) {
const range = this.ahead
? `${this.status.upstream}..${this.status.ref}`
: `${this.status.ref}..${this.status.upstream}`;
this._log = await Container.git.getLog(this.uri.repoPath!, {
limit: this.view.config.defaultItemLimit,
ref: range
});
}
return this._log;
}
get hasMore() {
return this._log?.hasMore ?? true;
}
async showMore(limit?: number | { until?: any }) {
let log = await this.getLog();
if (log === undefined || !log.hasMore) return;
log = await log.more?.(limit ?? this.view.config.pageItemLimit);
if (this._log === log) return;
this._log = log;
this.triggerChange(false);
}
}

+ 1
- 1
src/views/nodes/commitFileNode.ts 查看文件

@ -45,7 +45,7 @@ export class CommitFileNode extends ViewRefFileNode {
const commit = this.commit.toFileCommit(this.file);
if (commit === undefined) {
const log = await Container.git.getLogForFile(this.repoPath, this.file.fileName, {
maxCount: 2,
limit: 2,
ref: this.commit.sha
});
if (log !== undefined) {

+ 0
- 5
src/views/nodes/common.ts 查看文件

@ -140,13 +140,10 @@ export abstract class PagerNode extends ViewNode {
view: View,
parent: ViewNode & PageableViewNode,
protected readonly message: string,
maxCount: number | undefined,
private readonly _previousNode?: ViewNode,
private readonly _pageSize: number = Container.config.views.pageItemLimit
) {
super(unknownGitUri, view, parent);
parent.maxCount = maxCount;
}
showMore() {
@ -190,7 +187,6 @@ export class ShowMoreNode extends PagerNode {
view: View,
parent: ViewNode & PageableViewNode,
itemType: string,
maxCount: number | undefined,
previousNode: ViewNode,
pageSize?: number
) {
@ -200,7 +196,6 @@ export class ShowMoreNode extends PagerNode {
pageSize === 0
? `Show All ${itemType} ${GlyphChars.Space}${GlyphChars.Dash}${GlyphChars.Space} this may take a while`
: `Show More ${itemType}`,
maxCount,
previousNode,
pageSize
);

+ 19
- 9
src/views/nodes/compareBranchNode.ts 查看文件

@ -7,7 +7,7 @@ import { GitBranch, GitService, GitUri } from '../../git/gitService';
import { CommandQuickPickItem, ReferencesQuickPick } from '../../quickpicks';
import { CommitsQueryResults, ResultsCommitsNode } from './resultsCommitsNode';
import { Container } from '../../container';
import { log, Strings } from '../../system';
import { log, Mutable, Strings } from '../../system';
import { FilesQueryResults, ResultsFilesNode } from './resultsFilesNode';
import { ViewShowBranchComparison } from '../../config';
import { RepositoryNode } from './repositoryNode';
@ -188,21 +188,31 @@ export class CompareBranchNode extends ViewNode {
this.view.triggerNodeChange(this);
}
private async getCommitsQuery(maxCount: number | undefined): Promise<CommitsQueryResults> {
private async getCommitsQuery(limit: number | undefined): Promise<CommitsQueryResults> {
const log = await Container.git.getLog(this.uri.repoPath!, {
maxCount: maxCount,
limit: limit,
ref: `${(this._compareWith && this._compareWith.ref) || 'HEAD'}${this.comparisonNotation}${
this.compareWithWorkingTree ? '' : this.branch.ref
}`
});
const count = log !== undefined ? log.count : 0;
const truncated = log !== undefined ? log.truncated : false;
return {
label: Strings.pluralize('commit', count, { number: truncated ? `${count}+` : undefined, zero: 'No' }),
log: log
const count = log?.count ?? 0;
const results: Mutable<Partial<CommitsQueryResults>> = {
label: Strings.pluralize('commit', count, { number: log?.hasMore ?? false ? `${count}+` : undefined, zero: 'No' }),
log: log,
hasMore: log?.hasMore ?? true
};
if (results.hasMore) {
results.more = async (limit: number | undefined) => {
results.log = (await results.log?.more?.(limit)) ?? results.log;
const count = results.log?.count ?? 0;
results.label = Strings.pluralize('commit', count, { number: results.log?.hasMore ?? false ? `${count}+` : undefined, zero: 'No' });
results.hasMore = results.log?.hasMore ?? true;
};
}
return results as CommitsQueryResults;
}
private async getFilesQuery(): Promise<FilesQueryResults> {

+ 19
- 9
src/views/nodes/compareResultsNode.ts 查看文件

@ -3,7 +3,7 @@ import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { NamedRef } from '../../constants';
import { Container } from '../../container';
import { GitService, GitUri } from '../../git/gitService';
import { debug, gate, log, Strings } from '../../system';
import { debug, gate, log, Mutable, Strings } from '../../system';
import { CompareView } from '../compareView';
import { CommitsQueryResults, ResultsCommitsNode } from './resultsCommitsNode';
import { FilesQueryResults, ResultsFilesNode } from './resultsFilesNode';
@ -201,19 +201,29 @@ export class CompareResultsNode extends SubscribeableViewNode {
return this.comparisonNotation === '...' ? '..' : '...';
}
private async getCommitsQuery(maxCount: number | undefined): Promise<CommitsQueryResults> {
private async getCommitsQuery(limit: number | undefined): Promise<CommitsQueryResults> {
const log = await Container.git.getLog(this.uri.repoPath!, {
maxCount: maxCount,
limit: limit,
ref: `${this._compareWith.ref || 'HEAD'}${this.comparisonNotation}${this._ref.ref || 'HEAD'}`
});
const count = log !== undefined ? log.count : 0;
const truncated = log !== undefined ? log.truncated : false;
return {
label: Strings.pluralize('commit', count, { number: truncated ? `${count}+` : undefined, zero: 'No' }),
log: log
const count = log?.count ?? 0;
const results: Mutable<Partial<CommitsQueryResults>> = {
label: Strings.pluralize('commit', count, { number: log?.hasMore ?? false ? `${count}+` : undefined, zero: 'No' }),
log: log,
hasMore: log?.hasMore ?? true
};
if (results.hasMore) {
results.more = async (limit: number | undefined) => {
results.log = (await results.log?.more?.(limit)) ?? results.log;
const count = results.log?.count ?? 0;
results.label = Strings.pluralize('commit', count, { number: results.log?.hasMore ?? false ? `${count}+` : undefined, zero: 'No' });
results.hasMore = results.log?.hasMore ?? true;
};
}
return results as CommitsQueryResults;
}
private async getFilesQuery(): Promise<FilesQueryResults> {

+ 40
- 12
src/views/nodes/contributorNode.ts 查看文件

@ -1,7 +1,7 @@
'use strict';
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { GitContributor, GitUri } from '../../git/gitService';
import { Iterables, Strings } from '../../system';
import { GitContributor, GitLog, GitUri } from '../../git/gitService';
import { debug, gate, Iterables, Strings } from '../../system';
import { RepositoriesView } from '../repositoriesView';
import { PageableViewNode, ResourceType, ViewNode } from './viewNode';
import { Container } from '../../container';
@ -17,10 +17,6 @@ export class ContributorNode extends ViewNode implements Pagea
return `${RepositoryNode.getId(repoPath)}${this.key}(${name}|${email})`;
}
readonly supportsPaging = true;
readonly rememberLastMaxCount = true;
maxCount: number | undefined = this.view.getNodeLastMaxCount(this);
constructor(uri: GitUri, view: RepositoriesView, parent: ViewNode, public readonly contributor: GitContributor) {
super(uri, view, parent);
}
@ -34,10 +30,7 @@ export class ContributorNode extends ViewNode implements Pagea
}
async getChildren(): Promise<ViewNode[]> {
const log = await Container.git.getLog(this.uri.repoPath!, {
maxCount: this.maxCount !== undefined ? this.maxCount : this.view.config.defaultItemLimit,
authors: [`^${this.contributor.name} <${this.contributor.email}>$`]
});
const log = await this.getLog();
if (log === undefined) return [new MessageNode(this.view, this, 'No commits could be found.')];
const getBranchAndTagTips = await Container.git.getBranchesAndTagsTipsFn(this.uri.repoPath);
@ -51,8 +44,8 @@ export class ContributorNode extends ViewNode implements Pagea
)
];
if (log.truncated) {
children.push(new ShowMoreNode(this.view, this, 'Commits', log.maxCount, children[children.length - 1]));
if (log.hasMore) {
children.push(new ShowMoreNode(this.view, this, 'Commits', children[children.length - 1]));
}
return children;
}
@ -78,4 +71,39 @@ export class ContributorNode extends ViewNode implements Pagea
return item;
}
@gate()
@debug()
refresh(reset?: boolean) {
if (reset) {
this._log = undefined;
}
}
private _log: GitLog | undefined;
private async getLog() {
if (this._log === undefined) {
this._log = await Container.git.getLog(this.uri.repoPath!, {
limit: this.view.config.defaultItemLimit,
authors: [`^${this.contributor.name} <${this.contributor.email}>$`]
});
}
return this._log;
}
get hasMore() {
return this._log?.hasMore ?? true;
}
async showMore(limit?: number | { until?: any }) {
let log = await this.getLog();
if (log === undefined || !log.hasMore) return;
log = await log.more?.(limit ?? this.view.config.pageItemLimit);
if (this._log === log) return;
this._log = log;
this.triggerChange(false);
}
}

+ 40
- 12
src/views/nodes/fileHistoryNode.ts 查看文件

@ -3,6 +3,7 @@ import { Disposable, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { Container } from '../../container';
import {
GitCommitType,
GitLog,
GitLogCommit,
GitService,
GitUri,
@ -11,7 +12,7 @@ import {
RepositoryFileSystemChangeEvent
} from '../../git/gitService';
import { Logger } from '../../logger';
import { debug, Iterables } from '../../system';
import { debug, gate, Iterables } from '../../system';
import { View } from '../viewBase';
import { CommitFileNode } from './commitFileNode';
import { MessageNode, ShowMoreNode } from './common';
@ -19,9 +20,6 @@ import { insertDateMarkers } from './helpers';
import { PageableViewNode, ResourceType, SubscribeableViewNode, ViewNode } from './viewNode';
export class FileHistoryNode extends SubscribeableViewNode implements PageableViewNode {
readonly supportsPaging = true;
maxCount: number | undefined;
constructor(uri: GitUri, view: View, parent: ViewNode) {
super(uri, view, parent);
}
@ -73,10 +71,7 @@ export class FileHistoryNode extends SubscribeableViewNode implements PageableVi
}
}
const log = await Container.git.getLogForFile(this.uri.repoPath, this.uri.fsPath, {
maxCount: this.maxCount !== undefined ? this.maxCount : undefined,
ref: this.uri.sha
});
const log = await this.getLog();
if (log !== undefined) {
children.push(
...insertDateMarkers(
@ -92,10 +87,8 @@ export class FileHistoryNode extends SubscribeableViewNode implements PageableVi
)
);
if (log.truncated) {
children.push(
new ShowMoreNode(this.view, this, 'Commits', log.maxCount, children[children.length - 1])
);
if (log.hasMore) {
children.push(new ShowMoreNode(this.view, this, 'Commits', children[children.length - 1]));
}
}
@ -165,4 +158,39 @@ export class FileHistoryNode extends SubscribeableViewNode implements PageableVi
void this.triggerChange();
}
@gate()
@debug()
refresh(reset?: boolean) {
if (reset) {
this._log = undefined;
}
}
private _log: GitLog | undefined;
private async getLog() {
if (this._log === undefined) {
this._log = await Container.git.getLogForFile(this.uri.repoPath, this.uri.fsPath, {
limit: this.view.config.defaultItemLimit,
ref: this.uri.sha
});
}
return this._log;
}
get hasMore() {
return this._log?.hasMore ?? true;
}
async showMore(limit?: number | { until?: any }) {
let log = await this.getLog();
if (log === undefined || !log.hasMore) return;
log = await log.more?.(limit ?? this.view.config.pageItemLimit);
if (this._log === log) return;
this._log = log;
this.triggerChange(false);
}
}

+ 41
- 13
src/views/nodes/lineHistoryNode.ts 查看文件

@ -3,6 +3,7 @@ import { Disposable, Selection, TreeItem, TreeItemCollapsibleState } from 'vscod
import { Container } from '../../container';
import { GitCommitType, GitFile, GitLogCommit } from '../../git/git';
import {
GitLog,
GitService,
GitUri,
RepositoryChange,
@ -10,7 +11,7 @@ import {
RepositoryFileSystemChangeEvent
} from '../../git/gitService';
import { Logger } from '../../logger';
import { debug, Iterables } from '../../system';
import { debug, gate, Iterables } from '../../system';
import { View } from '../viewBase';
import { CommitFileNode } from './commitFileNode';
import { MessageNode, ShowMoreNode } from './common';
@ -18,9 +19,6 @@ import { insertDateMarkers } from './helpers';
import { PageableViewNode, ResourceType, SubscribeableViewNode, ViewNode } from './viewNode';
export class LineHistoryNode extends SubscribeableViewNode implements PageableViewNode {
readonly supportsPaging = true;
maxCount: number | undefined;
constructor(
uri: GitUri,
view: View,
@ -102,11 +100,7 @@ export class LineHistoryNode extends SubscribeableViewNode implements PageableVi
}
}
const log = await Container.git.getLogForFile(this.uri.repoPath, this.uri.fsPath, {
maxCount: this.maxCount !== undefined ? this.maxCount : undefined,
ref: this.uri.sha,
range: selection
});
const log = await this.getLog(selection);
if (log !== undefined) {
children.push(
...insertDateMarkers(
@ -123,10 +117,8 @@ export class LineHistoryNode extends SubscribeableViewNode implements PageableVi
)
);
if (log.truncated) {
children.push(
new ShowMoreNode(this.view, this, 'Commits', log.maxCount, children[children.length - 1])
);
if (log.hasMore) {
children.push(new ShowMoreNode(this.view, this, 'Commits', children[children.length - 1]));
}
}
@ -197,4 +189,40 @@ export class LineHistoryNode extends SubscribeableViewNode implements PageableVi
void this.triggerChange();
}
@gate()
@debug()
refresh(reset?: boolean) {
if (reset) {
this._log = undefined;
}
}
private _log: GitLog | undefined;
private async getLog(selection?: Selection) {
if (this._log === undefined) {
this._log = await Container.git.getLogForFile(this.uri.repoPath, this.uri.fsPath, {
limit: this.view.config.defaultItemLimit,
ref: this.uri.sha,
range: selection ?? this.selection
});
}
return this._log;
}
get hasMore() {
return this._log?.hasMore ?? true;
}
async showMore(limit?: number | { until?: any }) {
let log = await this.getLog();
if (log === undefined || !log.hasMore) return;
log = await log.more?.(limit ?? this.view.config.pageItemLimit);
if (this._log === log) return;
this._log = log;
this.triggerChange(false);
}
}

+ 35
- 12
src/views/nodes/reflogNode.ts 查看文件

@ -1,7 +1,7 @@
'use strict';
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { Container } from '../../container';
import { GitUri, Repository } from '../../git/gitService';
import { GitReflog, GitUri, Repository } from '../../git/gitService';
import { PageableViewNode, ResourceType, ViewNode } from './viewNode';
import { RepositoriesView } from '../repositoriesView';
import { ReflogRecordNode } from './reflogRecordNode';
@ -15,10 +15,6 @@ export class ReflogNode extends ViewNode implements PageableVi
return `${RepositoryNode.getId(repoPath)}${this.key}`;
}
readonly supportsPaging = true;
readonly rememberLastMaxCount = true;
maxCount: number | undefined = this.view.getNodeLastMaxCount(this);
private _children: ViewNode[] | undefined;
constructor(uri: GitUri, view: RepositoriesView, parent: ViewNode, public readonly repo: Repository) {
@ -33,19 +29,16 @@ export class ReflogNode extends ViewNode implements PageableVi
if (this._children === undefined) {
const children = [];
const reflog = await Container.git.getIncomingActivity(this.repo.path, {
all: true,
maxCount: this.maxCount !== undefined ? this.maxCount : this.view.config.defaultItemLimit
});
const reflog = await this.getReflog();
if (reflog === undefined || reflog.records.length === 0) {
return [new MessageNode(this.view, this, 'No activity could be found.')];
}
children.push(...reflog.records.map(r => new ReflogRecordNode(this.view, this, r)));
if (reflog.truncated) {
if (reflog.hasMore) {
children.push(
new ShowMoreNode(this.view, this, 'Activity', reflog.maxCount, children[children.length - 1])
new ShowMoreNode(this.view, this, 'Activity', children[children.length - 1])
);
}
@ -69,7 +62,37 @@ export class ReflogNode extends ViewNode implements PageableVi
@gate()
@debug()
refresh() {
refresh(reset?: boolean) {
this._children = undefined;
if (reset) {
this._reflog = undefined;
}
}
private _reflog: GitReflog | undefined;
private async getReflog() {
if (this._reflog === undefined) {
this._reflog = await Container.git.getIncomingActivity(this.repo.path, {
all: true,
limit: this.view.config.defaultItemLimit
});
}
return this._reflog;
}
get hasMore() {
return this._reflog?.hasMore ?? true;
}
async showMore(limit?: number) {
let reflog = await this.getReflog();
if (reflog === undefined || !reflog.hasMore) return;
reflog = await reflog.more?.(limit ?? this.view.config.pageItemLimit);
if (this._reflog === reflog) return;
this._reflog = reflog;
this.triggerChange(false);
}
}

+ 45
- 18
src/views/nodes/reflogRecordNode.ts 查看文件

@ -2,8 +2,8 @@
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { GlyphChars } from '../../constants';
import { Container } from '../../container';
import { GitReflogRecord, GitUri } from '../../git/gitService';
import { Iterables } from '../../system';
import { GitLog, GitReflogRecord, GitUri } from '../../git/gitService';
import { debug, gate, Iterables } from '../../system';
import { ViewWithFiles } from '../viewBase';
import { CommitNode } from './commitNode';
import { MessageNode, ShowMoreNode } from './common';
@ -24,10 +24,6 @@ export class ReflogRecordNode extends ViewNode implements Pageabl
''}|${date.getTime()})`;
}
readonly supportsPaging = true;
readonly rememberLastMaxCount = true;
maxCount: number | undefined = this.view.getNodeLastMaxCount(this);
constructor(view: ViewWithFiles, parent: ViewNode, public readonly record: GitReflogRecord) {
super(GitUri.fromRepoPath(record.repoPath), view, parent);
}
@ -44,20 +40,15 @@ export class ReflogRecordNode extends ViewNode implements Pageabl
}
async getChildren(): Promise<ViewNode[]> {
const range = `${this.record.previousSha}..${this.record.sha}`;
const log = await Container.git.getLog(this.uri.repoPath!, {
maxCount: this.maxCount !== undefined ? this.maxCount : this.view.config.defaultItemLimit,
ref: range
});
const log = await this.getLog();
if (log === undefined) return [new MessageNode(this.view, this, 'No commits could be found.')];
const children: (CommitNode | ShowMoreNode)[] = [
...Iterables.map(log.commits.values(), c => new CommitNode(this.view, this, c))
];
if (log.truncated) {
children.push(new ShowMoreNode(this.view, this, 'Commits', log.maxCount, children[children.length - 1]));
if (log.hasMore) {
children.push(new ShowMoreNode(this.view, this, 'Commits', children[children.length - 1]));
}
return children;
}
@ -72,16 +63,52 @@ export class ReflogRecordNode extends ViewNode implements Pageabl
this.record.HEAD.length === 0
? ''
: `${this.record.HEAD} ${GlyphChars.Space}${GlyphChars.Dot}${GlyphChars.Space} `
}${this.record.formattedDate}`;
}${this.record.formattedDate}`;
item.contextValue = ResourceType.ReflogRecord;
item.tooltip = `${this.record.HEAD.length === 0 ? '' : `${this.record.HEAD}\n`}${this.record.command}${
this.record.commandArgs ? ` ${this.record.commandArgs}` : ''
}${
}${
this.record.details ? ` (${this.record.details})` : ''
}\n${this.record.formatDateFromNow()} (${this.record.formatDate()})\n${this.record.previousShortSha} ${
}\n${this.record.formatDateFromNow()} (${this.record.formatDate()})\n${this.record.previousShortSha} ${
GlyphChars.Space
}${GlyphChars.ArrowRight}${GlyphChars.Space} ${this.record.shortSha}`;
}${GlyphChars.ArrowRight}${GlyphChars.Space} ${this.record.shortSha}`;
return item;
}
@gate()
@debug()
refresh(reset?: boolean) {
if (reset) {
this._log = undefined;
}
}
private _log: GitLog | undefined;
private async getLog() {
if (this._log === undefined) {
const range = `${this.record.previousSha}..${this.record.sha}`;
this._log = await Container.git.getLog(this.uri.repoPath!, {
limit: this.view.config.defaultItemLimit,
ref: range
});
}
return this._log;
}
get hasMore() {
return this._log?.hasMore ?? true;
}
async showMore(limit?: number | { until?: any }) {
let log = await this.getLog();
if (log === undefined || !log.hasMore) return;
log = await log.more?.(limit ?? this.view.config.pageItemLimit);
if (this._log === log) return;
this._log = log;
this.triggerChange(false);
}
}

+ 35
- 32
src/views/nodes/resultsCommitsNode.ts 查看文件

@ -10,21 +10,19 @@ import { insertDateMarkers } from './helpers';
import { PageableViewNode, ResourceType, ViewNode } from './viewNode';
export interface CommitsQueryResults {
label: string;
log: GitLog | undefined;
readonly label: string;
readonly log: GitLog | undefined;
readonly hasMore: boolean;
more?(limit: number | undefined): Promise<void>;
}
export class ResultsCommitsNode extends ViewNode<ViewWithFiles> implements PageableViewNode {
readonly supportsPaging = true;
readonly rememberLastMaxCount = true;
maxCount: number | undefined = this.view.getNodeLastMaxCount(this);
constructor(
view: ViewWithFiles,
parent: ViewNode,
public readonly repoPath: string,
private _label: string,
private readonly _commitsQuery: (maxCount: number | undefined) => Promise<CommitsQueryResults>,
private readonly _commitsQuery: (limit: number | undefined) => Promise<CommitsQueryResults>,
private readonly _options: { expand?: boolean; includeDescription?: boolean } = {}
) {
super(GitUri.fromRepoPath(repoPath), view, parent);
@ -59,9 +57,9 @@ export class ResultsCommitsNode extends ViewNode implements Pagea
)
];
if (log.truncated) {
if (log.hasMore) {
children.push(
new ShowMoreNode(this.view, this, 'Results', log.maxCount, children[children.length - 1], this.maxCount)
new ShowMoreNode(this.view, this, 'Results', children[children.length - 1])
);
}
@ -75,25 +73,14 @@ export class ResultsCommitsNode extends ViewNode implements Pagea
try {
({ label, log } = await Promises.timeout(this.getCommitsQueryResults(), 100));
if (log != null) {
this.maxCount = log.maxCount;
}
state =
log == null || log.count === 0
? TreeItemCollapsibleState.None
: this._options.expand || log.count === 1
? TreeItemCollapsibleState.Expanded
: TreeItemCollapsibleState.Collapsed;
state = log == null || log.count === 0
? TreeItemCollapsibleState.None
: this._options.expand || log.count === 1
? TreeItemCollapsibleState.Expanded
: TreeItemCollapsibleState.Collapsed;
} catch (ex) {
if (ex instanceof Promises.TimeoutError) {
ex.promise.then(({ log }: CommitsQueryResults) => {
if (log != null) {
this.maxCount = log.maxCount;
}
this.triggerChange(false);
});
ex.promise.then(() => this.triggerChange(false));
}
// Need to use Collapsed before we have results or the item won't show up in the view until the children are awaited
@ -118,18 +105,34 @@ export class ResultsCommitsNode extends ViewNode implements Pagea
@gate()
@debug()
refresh(reset: boolean = false) {
if (!reset) return;
this._commitsQueryResults = this._commitsQuery(this.maxCount);
if (reset) {
this._commitsQueryResults = undefined;
void this.getCommitsQueryResults();
}
}
private _commitsQueryResults: Promise<CommitsQueryResults> | undefined;
protected getCommitsQueryResults() {
private async getCommitsQueryResults() {
if (this._commitsQueryResults === undefined) {
this._commitsQueryResults = this._commitsQuery(this.maxCount);
this._commitsQueryResults = this._commitsQuery(Container.config.advanced.maxSearchItems);
const results = await this._commitsQueryResults;
this._hasMore = results.hasMore;
}
return this._commitsQueryResults;
}
private _hasMore = true;
get hasMore() {
return this._hasMore;
}
async showMore(limit?: number) {
const results = await this.getCommitsQueryResults();
if (results === undefined || !results.hasMore) return;
await results.more?.(limit ?? this.view.config.pageItemLimit);
this.triggerChange(false);
}
}

+ 1
- 1
src/views/nodes/searchResultsCommitsNode.ts 查看文件

@ -26,7 +26,7 @@ export class SearchResultsCommitsNode extends ResultsCommitsNode {
repoPath: string,
public readonly search: SearchPattern,
label: string,
commitsQuery: (maxCount: number | undefined) => Promise<CommitsQueryResults>
commitsQuery: (limit: number | undefined) => Promise<CommitsQueryResults>
) {
super(view, parent, repoPath, label, commitsQuery, {
expand: true,

+ 1
- 1
src/views/nodes/stashNode.ts 查看文件

@ -36,7 +36,7 @@ export class StashNode extends ViewRefNode {
// Check for any untracked files -- since git doesn't return them via `git stash list` :(
// See https://stackoverflow.com/questions/12681529/
const log = await Container.git.getLog(this.commit.repoPath, {
maxCount: 1,
limit: 1,
ref: `${this.commit.stashName}^3`
});
if (log !== undefined) {

+ 1
- 1
src/views/nodes/statusFilesNode.ts 查看文件

@ -49,7 +49,7 @@ export class StatusFilesNode extends ViewNode {
let log: GitLog | undefined;
if (this.range !== undefined) {
log = await Container.git.getLog(repoPath, { maxCount: 0, ref: this.range });
log = await Container.git.getLog(repoPath, { limit: 0, ref: this.range });
if (log !== undefined) {
files = [
...Iterables.flatMap(log.commits.values(), c =>

+ 40
- 12
src/views/nodes/tagNode.ts 查看文件

@ -2,8 +2,8 @@
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { ViewBranchesLayout } from '../../configuration';
import { Container } from '../../container';
import { GitService, GitTag, GitUri, TagDateFormatting } from '../../git/gitService';
import { Iterables, Strings } from '../../system';
import { GitLog, GitService, GitTag, GitUri, TagDateFormatting } from '../../git/gitService';
import { debug, gate, Iterables, Strings } from '../../system';
import { RepositoriesView } from '../repositoriesView';
import { CommitNode } from './commitNode';
import { MessageNode, ShowMoreNode } from './common';
@ -19,10 +19,6 @@ export class TagNode extends ViewRefNode implements PageableVi
return `${RepositoryNode.getId(repoPath)}${this.key}(${name})`;
}
readonly supportsPaging = true;
readonly rememberLastMaxCount = true;
maxCount: number | undefined = this.view.getNodeLastMaxCount(this);
constructor(uri: GitUri, view: RepositoriesView, parent: ViewNode, public readonly tag: GitTag) {
super(uri, view, parent);
}
@ -44,10 +40,7 @@ export class TagNode extends ViewRefNode implements PageableVi
}
async getChildren(): Promise<ViewNode[]> {
const log = await Container.git.getLog(this.uri.repoPath!, {
maxCount: this.maxCount !== undefined ? this.maxCount : this.view.config.defaultItemLimit,
ref: this.tag.name
});
const log = await this.getLog();
if (log === undefined) return [new MessageNode(this.view, this, 'No commits could be found.')];
const getBranchAndTagTips = await Container.git.getBranchesAndTagsTipsFn(this.uri.repoPath, this.tag.name);
@ -61,8 +54,8 @@ export class TagNode extends ViewRefNode implements PageableVi
)
];
if (log.truncated) {
children.push(new ShowMoreNode(this.view, this, 'Commits', log.maxCount, children[children.length - 1]));
if (log.hasMore) {
children.push(new ShowMoreNode(this.view, this, 'Commits', children[children.length - 1]));
}
return children;
}
@ -88,4 +81,39 @@ export class TagNode extends ViewRefNode implements PageableVi
return item;
}
@gate()
@debug()
refresh(reset?: boolean) {
if (reset) {
this._log = undefined;
}
}
private _log: GitLog | undefined;
private async getLog() {
if (this._log === undefined) {
this._log = await Container.git.getLog(this.uri.repoPath!, {
limit: this.view.config.defaultItemLimit,
ref: this.tag.name
});
}
return this._log;
}
get hasMore() {
return this._log?.hasMore ?? true;
}
async showMore(limit?: number | { until?: any }) {
let log = await this.getLog();
if (log === undefined || !log.hasMore) return;
log = await log.more?.(limit ?? this.view.config.pageItemLimit);
if (this._log === log) return;
this._log = log;
this.triggerChange(false);
}
}

+ 3
- 5
src/views/nodes/viewNode.ts 查看文件

@ -118,15 +118,13 @@ export function nodeSupportsConditionalDismissal(node: ViewNode): node is ViewNo
}
export interface PageableViewNode {
readonly id?: string;
readonly supportsPaging: boolean;
readonly rememberLastMaxCount?: boolean;
maxCount: number | undefined;
readonly hasMore: boolean;
showMore(limit?: number | { until?: any }): Promise<void>;
}
export namespace PageableViewNode {
export function is(node: ViewNode): node is ViewNode & PageableViewNode {
return Functions.is<ViewNode & PageableViewNode>(node, 'supportsPaging', true);
return Functions.is<ViewNode & PageableViewNode>(node, 'showMore');
}
}

+ 5
- 2
src/views/repositoriesView.ts 查看文件

@ -208,12 +208,15 @@ export class RepositoriesView extends ViewBase {
return this.findNode((n: any) => n.commit !== undefined && n.commit.ref === commit.ref, {
allowPaging: true,
maxDepth: 6,
canTraverse: n => {
canTraverse: async n => {
// Only search for commit nodes in the same repo within BranchNodes
if (n instanceof RepositoriesNode) return true;
if (n instanceof BranchNode) {
return n.id.startsWith(repoNodeId) && branches.includes(n.branch.name);
if (n.id.startsWith(repoNodeId) && branches.includes(n.branch.name)) {
await n.showMore({ until: commit.ref });
return true;
}
}
if (

+ 47
- 25
src/views/searchView.ts 查看文件

@ -4,13 +4,15 @@ import { configuration, SearchViewConfig, ViewFilesLayout, ViewsConfig } from '.
import { CommandContext, setCommandContext, WorkspaceState } from '../constants';
import { Container } from '../container';
import { GitLog, SearchPattern } from '../git/gitService';
import { Functions, Strings } from '../system';
import { Functions, Mutable, Strings } from '../system';
import { nodeSupportsConditionalDismissal, SearchNode, SearchResultsCommitsNode, ViewNode } from './nodes';
import { ViewBase } from './viewBase';
interface SearchQueryResult {
label: string;
log: GitLog | undefined;
interface SearchQueryResults {
readonly label: string;
readonly log: GitLog | undefined;
readonly hasMore: boolean;
more?(limit: number | undefined): Promise<void>;
}
export class SearchView extends ViewBase<SearchNode> {
@ -116,7 +118,7 @@ export class SearchView extends ViewBase {
label: string;
resultsType?: { singular: string; plural: string };
};
maxCount?: number;
limit?: number;
},
results?: Promise<GitLog | undefined> | GitLog
) {
@ -144,7 +146,7 @@ export class SearchView extends ViewBase {
showSearchResults(
repoPath: string,
search: SearchPattern,
results: GitLog,
log: GitLog,
{
label,
...options
@ -155,16 +157,27 @@ export class SearchView extends ViewBase {
label: string;
resultsType?: { singular: string; plural: string };
};
maxCount?: number;
limit?: number;
}
) {
label = this.getSearchLabel(label, results);
const searchQueryFn = Functions.cachedOnce(this.getSearchQueryFn(results, { label: label, ...options }), {
label: label,
log: results
});
const labelString = this.getSearchLabel(label, log);
const results: Mutable<Partial<SearchQueryResults>> = {
label: labelString,
log: log,
hasMore: log.hasMore
};
if (results.hasMore) {
results.more = async (limit: number | undefined) => {
results.log = (await results.log?.more?.(limit)) ?? results.log;
results.label = this.getSearchLabel(label, results.log);
results.hasMore = results.log?.hasMore ?? true;
};
}
return this.addResults(new SearchResultsCommitsNode(this, this._root!, repoPath, search, label, searchQueryFn));
const searchQueryFn = Functions.cachedOnce(this.getSearchQueryFn(log, { label: label, ...options }), results as SearchQueryResults);
return this.addResults(new SearchResultsCommitsNode(this, this._root!, repoPath, search, labelString, searchQueryFn));
}
private addResults(results: ViewNode) {
@ -185,21 +198,20 @@ export class SearchView extends ViewBase {
) {
if (typeof label === 'string') return label;
const count = log !== undefined ? log.count : 0;
const truncated = log !== undefined ? log.truncated : false;
const count = log?.count ?? 0;
const resultsType =
label.resultsType === undefined ? { singular: 'result', plural: 'results' } : label.resultsType;
return `${Strings.pluralize(resultsType.singular, count, {
number: truncated ? `${count}+` : undefined,
number: log?.hasMore ?? false ? `${count}+` : undefined,
plural: resultsType.plural,
zero: 'No'
})} ${label.label}`;
}
private getSearchQueryFn(
results: Promise<GitLog | undefined> | GitLog | undefined,
logOrPromise: Promise<GitLog | undefined> | GitLog | undefined,
options: {
label:
| string
@ -208,22 +220,32 @@ export class SearchView extends ViewBase {
resultsType?: { singular: string; plural: string };
};
}
): (maxCount: number | undefined) => Promise<SearchQueryResult> {
): (limit: number | undefined) => Promise<SearchQueryResults> {
let useCacheOnce = true;
return async (maxCount: number | undefined) => {
let log = await results;
return async (limit: number | undefined) => {
let log = await logOrPromise;
if (!useCacheOnce && log !== undefined && log.query !== undefined) {
log = await log.query(maxCount);
log = await log.query(limit);
}
useCacheOnce = false;
const label = this.getSearchLabel(options.label, log);
return {
label: label,
log: log
const results: Mutable<Partial<SearchQueryResults>> = {
label: this.getSearchLabel(options.label, log),
log: log,
hasMore: log?.hasMore
};
if (results.hasMore) {
results.more = async (limit: number | undefined) => {
results.log = (await results.log?.more?.(limit)) ?? results.log;
results.label = this.getSearchLabel(options.label, results.log);
results.hasMore = results.log?.hasMore ?? true;
};
}
return results as SearchQueryResults;
};
}

+ 19
- 104
src/views/viewBase.ts 查看文件

@ -19,7 +19,7 @@ import {
import { configuration } from '../configuration';
import { Container } from '../container';
import { Logger } from '../logger';
import { debug, Functions, log, Strings } from '../system';
import { debug, Functions, log, Promises, Strings } from '../system';
import { CompareView } from './compareView';
import { FileHistoryView } from './fileHistoryView';
import { LineHistoryView } from './lineHistoryView';
@ -51,7 +51,6 @@ export abstract class ViewBase> implements TreeData
}
protected _disposable: Disposable | undefined;
private readonly _lastMaxCounts = new Map<string, number | undefined>();
protected _root: TRoot | undefined;
protected _tree: TreeView<ViewNode> | undefined;
@ -138,11 +137,6 @@ export abstract class ViewBase> implements TreeData
}
protected onElementCollapsed(e: TreeViewExpansionEvent<ViewNode>) {
// Clear any last max count if the node was collapsed
if (PageableViewNode.is(e.element)) {
this.resetNodeLastMaxCount(e.element);
}
this._onDidChangeNodeState.fire({ ...e, state: TreeItemCollapsibleState.Collapsed });
}
@ -168,7 +162,7 @@ export abstract class ViewBase> implements TreeData
id: string,
options?: {
allowPaging?: boolean;
canTraverse?: (node: ViewNode) => boolean;
canTraverse?: (node: ViewNode) => boolean | Promise<boolean>;
maxDepth?: number;
token?: CancellationToken;
}
@ -177,7 +171,7 @@ export abstract class ViewBase> implements TreeData
predicate: (node: ViewNode) => boolean,
options?: {
allowPaging?: boolean;
canTraverse?: (node: ViewNode) => boolean;
canTraverse?: (node: ViewNode) => boolean | Promise<boolean>;
maxDepth?: number;
token?: CancellationToken;
}
@ -188,7 +182,7 @@ export abstract class ViewBase> implements TreeData
typeof predicate === 'string' ? predicate : 'function',
1: (opts: {
allowPaging?: boolean;
canTraverse?: (node: ViewNode) => boolean;
canTraverse?: (node: ViewNode) => boolean | Promise<boolean>;
maxDepth?: number;
token?: CancellationToken;
}) => `options=${JSON.stringify({ ...opts, canTraverse: undefined, token: undefined })}`
@ -203,7 +197,7 @@ export abstract class ViewBase> implements TreeData
token
}: {
allowPaging?: boolean;
canTraverse?: (node: ViewNode) => boolean;
canTraverse?: (node: ViewNode) => boolean | Promise<boolean>;
maxDepth?: number;
token?: CancellationToken;
} = {}
@ -215,14 +209,6 @@ export abstract class ViewBase> implements TreeData
await this.show();
}
// const node = await this.findNodeCoreDFS(
// typeof predicate === 'string' ? n => n.id === predicate : predicate,
// await this.ensureRoot().getChildren(),
// allowPaging,
// canTraverse,
// maxDepth
// );
try {
const node = await this.findNodeCoreBFS(
typeof predicate === 'string' ? n => n.id === predicate : predicate,
@ -240,58 +226,11 @@ export abstract class ViewBase> implements TreeData
}
}
// private async findNodeCoreDFS(
// predicate: (node: ViewNode) => boolean,
// nodes: ViewNode[],
// allowPaging: boolean,
// canTraverse: ((node: ViewNode) => boolean) | undefined,
// depth: number
// ): Promise<ViewNode | undefined> {
// if (depth === 0) return undefined;
// const defaultPageSize = Container.config.advanced.maxListItems;
// let child;
// let children;
// for (const node of nodes) {
// if (canTraverse !== undefined && !canTraverse(node)) continue;
// children = await node.getChildren();
// if (children.length === 0) continue;
// child = children.find(predicate);
// if (child !== undefined) return child;
// if (PageableViewNode.is(node)) {
// if (node.maxCount !== 0 && allowPaging) {
// let pageSize = defaultPageSize === 0 ? 0 : (node.maxCount || 0) + defaultPageSize;
// while (true) {
// await this.showMoreNodeChildren(node, pageSize);
// child = (await node.getChildren()).find(predicate);
// if (child !== undefined) return child;
// if (pageSize === 0) break;
// pageSize = 0;
// }
// }
// // Don't traverse into paged children
// continue;
// }
// return this.findNodeCoreDFS(predicate, children, allowPaging, canTraverse, depth - 1);
// }
// return undefined;
// }
private async findNodeCoreBFS(
predicate: (node: ViewNode) => boolean,
root: ViewNode,
allowPaging: boolean,
canTraverse: ((node: ViewNode) => boolean) | undefined,
canTraverse: ((node: ViewNode) => boolean | Promise<boolean>) | undefined,
maxDepth: number,
token: CancellationToken | undefined
): Promise<ViewNode | undefined> {
@ -317,7 +256,14 @@ export abstract class ViewBase> implements TreeData
}
if (predicate(node)) return node;
if (canTraverse !== undefined && !canTraverse(node)) continue;
if (canTraverse !== undefined) {
const traversable = canTraverse(node);
if (Promises.is(traversable)) {
if (!(await traversable)) continue;
} else if (!traversable) {
continue;
}
}
children = await node.getChildren();
if (children.length === 0) continue;
@ -326,12 +272,11 @@ export abstract class ViewBase> implements TreeData
let child = children.find(predicate);
if (child !== undefined) return child;
if (node.maxCount !== 0 && allowPaging) {
let pageSize = defaultPageSize === 0 ? 0 : (node.maxCount || 0) + defaultPageSize;
if (allowPaging && node.hasMore) {
while (true) {
if (token !== undefined && token.isCancellationRequested) return undefined;
await this.showMoreNodeChildren(node, pageSize);
await this.showMoreNodeChildren(node, defaultPageSize);
pagedChildren = await Functions.cancellable(
Promise.resolve(node.getChildren()),
@ -344,9 +289,7 @@ export abstract class ViewBase> implements TreeData
child = pagedChildren.find(predicate);
if (child !== undefined) return child;
if (pageSize === 0) break;
pageSize = 0;
if (!node.hasMore) break;
}
}
@ -429,22 +372,6 @@ export abstract class ViewBase> implements TreeData
}
@debug({
args: { 0: (n: ViewNode) => n.toString() }
})
getNodeLastMaxCount(node: PageableViewNode) {
return node.id === undefined ? undefined : this._lastMaxCounts.get(node.id);
}
@debug({
args: { 0: (n: ViewNode) => n.toString() }
})
resetNodeLastMaxCount(node: PageableViewNode) {
if (node.id === undefined || !node.rememberLastMaxCount) return;
this._lastMaxCounts.delete(node.id);
}
@debug({
args: {
0: (n: ViewNode & PageableViewNode) => n.toString(),
3: (n?: ViewNode) => (n === undefined ? '' : n.toString())
@ -452,26 +379,14 @@ export abstract class ViewBase> implements TreeData
})
async showMoreNodeChildren(
node: ViewNode & PageableViewNode,
maxCount: number | undefined,
limit: number | { until: any } | undefined,
previousNode?: ViewNode
) {
if (node.maxCount === maxCount) return Promise.resolve();
if (maxCount === undefined || maxCount === 0) {
node.maxCount = maxCount;
} else {
node.maxCount = (node.maxCount || maxCount) + maxCount;
}
if (node.rememberLastMaxCount) {
this._lastMaxCounts.set(node.id!, node.maxCount);
}
if (previousNode !== undefined) {
void (await this.reveal(previousNode, { select: true }));
}
return this.refreshNode(node);
return node.showMore(limit);
}
@debug({

+ 0
- 6
src/views/viewCommands.ts 查看文件

@ -26,7 +26,6 @@ import {
FileHistoryNode,
FolderNode,
LineHistoryNode,
PageableViewNode,
PagerNode,
RemoteNode,
RepositoryNode,
@ -68,11 +67,6 @@ export class ViewCommands {
commands.registerCommand(
'gitlens.views.refreshNode',
(node: ViewNode, reset?: boolean) => {
if (reset == null && PageableViewNode.is(node)) {
node.maxCount = undefined;
node.view.resetNodeLastMaxCount(node);
}
return node.view.refreshNode(node, reset == null ? true : reset);
},
this

||||||
x
 
000:0
Loading…
取消
儲存