diff --git a/README.md b/README.md
index 992f952..725020f 100644
--- a/README.md
+++ b/README.md
@@ -1005,6 +1005,7 @@ See also [View Settings](#view-settings- 'Jump to the View settings')
| `gitlens.advanced.blame.delayAfterEdit` | Specifies the time (in milliseconds) to wait before re-blaming an unsaved document after an edit. Use 0 to specify an infinite wait |
| `gitlens.advanced.blame.sizeThresholdAfterEdit` | Specifies the maximum document size (in lines) allowed to be re-blamed after an edit while still unsaved. Use 0 to specify no maximum |
| `gitlens.advanced.caching.enabled` | Specifies whether git output will be cached — changing the default is not recommended |
+| `gitlens.advanced.commitOrdering` | Specifies the order by which commits will be shown. If unspecified, commits will be shown in reverse chronological order
`date` - shows commits in reverse chronological order of the commit timestamp
`author-date` - shows commits in reverse chronological order of the author timestamp
`topo` - shows commits in reverse chronological order of the commit timestamp, but avoids intermixing multiple lines of history |
| `gitlens.advanced.externalDiffTool` | Specifies an optional external diff tool to use when comparing files. Must be a configured [Git difftool](https://git-scm.com/docs/git-config#Documentation/git-config.txt-difftool). |
| `gitlens.advanced.externalDirectoryDiffTool` | Specifies an optional external diff tool to use when comparing directories. Must be a configured [Git difftool](https://git-scm.com/docs/git-config#Documentation/git-config.txt-difftool). |
| `gitlens.advanced.fileHistoryFollowsRenames` | Specifies whether file histories will follow renames -- will affect how merge commits are shown in histories |
diff --git a/package.json b/package.json
index 250fc22..910b931 100644
--- a/package.json
+++ b/package.json
@@ -2520,6 +2520,27 @@
"markdownDescription": "Specifies whether git output will be cached — changing the default is not recommended",
"scope": "window"
},
+ "gitlens.advanced.commitOrdering": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "default": null,
+ "enum": [
+ null,
+ "date",
+ "author-date",
+ "topo"
+ ],
+ "enumDescriptions": [
+ "Shows commits in reverse chronological order",
+ "Shows commits in reverse chronological order of the commit timestamp",
+ "Shows commits in reverse chronological order of the author timestamp",
+ "Shows commits in reverse chronological order of the commit timestamp, but avoids intermixing multiple lines of history"
+ ],
+ "markdownDescription": "Specifies the order by which commits will be shown. If unspecified, commits will be shown in reverse chronological order",
+ "scope": "window"
+ },
"gitlens.advanced.externalDiffTool": {
"type": [
"string",
diff --git a/src/config.ts b/src/config.ts
index 9ed9183..18599ec 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -308,6 +308,7 @@ export interface AdvancedConfig {
caching: {
enabled: boolean;
};
+ commitOrdering: string | null;
externalDiffTool: string | null;
externalDirectoryDiffTool: string | null;
fileHistoryFollowsRenames: boolean;
diff --git a/src/git/git.ts b/src/git/git.ts
index ab9c9ec..43e0b01 100644
--- a/src/git/git.ts
+++ b/src/git/git.ts
@@ -745,6 +745,7 @@ export namespace Git {
format = 'default',
limit,
merges,
+ ordering,
reverse,
similarityThreshold,
since,
@@ -753,6 +754,7 @@ export namespace Git {
format?: 'refs' | 'default';
limit?: number;
merges?: boolean;
+ ordering?: string | null;
reverse?: boolean;
similarityThreshold?: number | null;
since?: string;
@@ -770,6 +772,10 @@ export namespace Git {
params.push('--name-status');
}
+ if (ordering) {
+ params.push(`--${ordering}-order`);
+ }
+
if (limit && !reverse) {
params.push(`-n${limit + 1}`);
}
@@ -812,6 +818,7 @@ export namespace Git {
firstParent = false,
format = 'default',
limit,
+ ordering,
renames = true,
reverse = false,
since,
@@ -824,6 +831,7 @@ export namespace Git {
firstParent?: boolean;
format?: 'refs' | 'simple' | 'default';
limit?: number;
+ ordering?: string | null;
renames?: boolean;
reverse?: boolean;
since?: string;
@@ -839,6 +847,10 @@ export namespace Git {
`--format=${format === 'default' ? GitLogParser.defaultFormat : GitLogParser.simpleFormat}`,
];
+ if (ordering) {
+ params.push(`--${ordering}-order`);
+ }
+
if (limit && !reverse) {
params.push(`-n${limit + 1}`);
}
@@ -903,7 +915,11 @@ export namespace Git {
export async function log__file_recent(
repoPath: string,
fileName: string,
- { ref, similarityThreshold }: { ref?: string; similarityThreshold?: number | null } = {},
+ {
+ ordering,
+ ref,
+ similarityThreshold,
+ }: { ordering?: string | null; ref?: string; similarityThreshold?: number | null } = {},
) {
const params = [
'log',
@@ -912,6 +928,10 @@ export namespace Git {
'--format=%H',
];
+ if (ordering) {
+ params.push(`--${ordering}-order`);
+ }
+
if (ref) {
params.push(ref);
}
@@ -925,8 +945,19 @@ export namespace Git {
return data.length === 0 ? undefined : data.trim();
}
- export async function log__find_object(repoPath: string, objectId: string, ref: string, file?: string) {
+ export async function log__find_object(
+ repoPath: string,
+ objectId: string,
+ ref: string,
+ ordering: string | null,
+ file?: string,
+ ) {
const params = ['log', '-n1', '--no-renames', '--format=%H', `--find-object=${objectId}`, ref];
+
+ if (ordering) {
+ params.push(`--${ordering}-order`);
+ }
+
if (file) {
params.push('--', file);
}
@@ -938,32 +969,47 @@ export namespace Git {
return data.length === 0 ? undefined : data.trim();
}
- export async function log__recent(repoPath: string) {
+ export async function log__recent(repoPath: string, ordering?: string | null) {
+ const params = ['log', '-n1', '--format=%H'];
+
+ if (ordering) {
+ params.push(`--${ordering}-order`);
+ }
+
const data = await git(
{ cwd: repoPath, configs: ['-c', 'log.showSignature=false'], errors: GitErrorHandling.Ignore },
- 'log',
- '-n1',
- '--format=%H',
+ ...params,
'--',
);
+
return data.length === 0 ? undefined : data.trim();
}
- export async function log__recent_committerdate(repoPath: string) {
+ export async function log__recent_committerdate(repoPath: string, ordering?: string | null) {
+ const params = ['log', '-n1', '--format=%ct'];
+
+ if (ordering) {
+ params.push(`--${ordering}-order`);
+ }
+
const data = await git(
{ cwd: repoPath, configs: ['-c', 'log.showSignature=false'], errors: GitErrorHandling.Ignore },
- 'log',
- '-n1',
- '--format=%ct',
+ ...params,
'--',
);
+
return data.length === 0 ? undefined : data.trim();
}
export function log__search(
repoPath: string,
search: string[] = emptyArray,
- { limit, skip, useShow }: { limit?: number; skip?: number; useShow?: boolean } = {},
+ {
+ limit,
+ ordering,
+ skip,
+ useShow,
+ }: { limit?: number; ordering?: string | null; skip?: number; useShow?: boolean } = {},
) {
const params = [
useShow ? 'show' : 'log',
@@ -971,13 +1017,19 @@ export namespace Git {
`--format=${GitLogParser.defaultFormat}`,
'--use-mailmap',
];
+
if (limit && !useShow) {
params.push(`-n${limit + 1}`);
}
+
if (skip && !useShow) {
params.push(`--skip=${skip}`);
}
+ if (ordering && !useShow) {
+ params.push(`--${ordering}-order`);
+ }
+
return git(
{ cwd: repoPath, configs: useShow ? undefined : ['-c', 'log.showSignature=false'] },
...params,
@@ -1038,18 +1090,32 @@ export namespace Git {
export function reflog(
repoPath: string,
- { all, branch, limit, skip }: { all?: boolean; branch?: string; limit?: number; skip?: number } = {},
+ {
+ all,
+ branch,
+ limit,
+ ordering,
+ skip,
+ }: { all?: boolean; branch?: string; limit?: number; ordering?: string | null; skip?: number } = {},
): Promise {
const params = ['log', '--walk-reflogs', `--format=${GitReflogParser.defaultFormat}`, '--date=iso8601'];
+
+ if (ordering) {
+ params.push(`--${ordering}-order`);
+ }
+
if (all) {
params.push('--all');
}
+
if (limit) {
params.push(`-n${limit}`);
}
+
if (skip) {
params.push(`--skip=${skip}`);
}
+
if (branch) {
params.push(branch);
}
@@ -1122,6 +1188,7 @@ export namespace Git {
export async function rev_parse__currentBranch(
repoPath: string,
+ ordering: string | null,
): Promise<[string, string | undefined] | undefined> {
try {
const data = await git(
@@ -1164,7 +1231,7 @@ export namespace Git {
}
if (GitWarnings.headNotABranch.test(msg)) {
- const sha = await log__recent(repoPath);
+ const sha = await log__recent(repoPath, ordering);
if (sha === undefined) return undefined;
return [`(HEAD detached at ${GitRevision.shorten(sha)})`, sha];
diff --git a/src/git/gitService.ts b/src/git/gitService.ts
index d5e94d4..1e62293 100644
--- a/src/git/gitService.ts
+++ b/src/git/gitService.ts
@@ -1215,14 +1215,14 @@ export class GitService implements Disposable {
let [branch] = await this.getBranches(repoPath, { filter: b => b.current });
if (branch != null) return branch;
- const data = await Git.rev_parse__currentBranch(repoPath);
+ const data = await Git.rev_parse__currentBranch(repoPath, Container.config.advanced.commitOrdering);
if (data == null) return undefined;
const [name, tracking] = data[0].split('\n');
if (GitBranch.isDetached(name)) {
const [rebaseStatus, committerDate] = await Promise.all([
this.getRebaseStatus(repoPath),
- Git.log__recent_committerdate(repoPath),
+ Git.log__recent_committerdate(repoPath, Container.config.advanced.commitOrdering),
]);
branch = new GitBranch(
@@ -1300,12 +1300,12 @@ export class GitService implements Disposable {
if (data == null || data.length === 0) {
let current;
- const data = await Git.rev_parse__currentBranch(repoPath);
+ const data = await Git.rev_parse__currentBranch(repoPath, Container.config.advanced.commitOrdering);
if (data != null) {
const [name, tracking] = data[0].split('\n');
const [rebaseStatus, committerDate] = await Promise.all([
GitBranch.isDetached(name) ? this.getRebaseStatus(repoPath) : undefined,
- Git.log__recent_committerdate(repoPath),
+ Git.log__recent_committerdate(repoPath, Container.config.advanced.commitOrdering),
]);
current = new GitBranch(
@@ -1498,6 +1498,7 @@ export class GitService implements Disposable {
async getOldestUnpushedRefForFile(repoPath: string, fileName: string): Promise {
const data = await Git.log__file(repoPath, fileName, '@{push}..', {
format: 'refs',
+ ordering: Container.config.advanced.commitOrdering,
renames: true,
});
if (data == null || data.length === 0) return undefined;
@@ -1876,6 +1877,7 @@ export class GitService implements Disposable {
authors?: string[];
limit?: number;
merges?: boolean;
+ ordering?: string | null;
ref?: string;
reverse?: boolean;
since?: string;
@@ -1888,6 +1890,7 @@ export class GitService implements Disposable {
authors: options.authors,
limit: limit,
merges: options.merges == null ? true : options.merges,
+ ordering: options.ordering ?? Container.config.advanced.commitOrdering,
reverse: options.reverse,
similarityThreshold: Container.config.advanced.similarityThreshold,
since: options.since,
@@ -1928,6 +1931,7 @@ export class GitService implements Disposable {
authors?: string[];
limit?: number;
merges?: boolean;
+ ordering?: string | null;
ref?: string;
reverse?: boolean;
since?: string;
@@ -1944,6 +1948,7 @@ export class GitService implements Disposable {
reverse: options.reverse,
similarityThreshold: Container.config.advanced.similarityThreshold,
since: options.since,
+ ordering: options.ordering ?? Container.config.advanced.commitOrdering,
});
const commits = GitLogParser.parseRefsOnly(data);
return new Set(commits);
@@ -1954,7 +1959,14 @@ export class GitService implements Disposable {
private getLogMoreFn(
log: GitLog,
- options: { authors?: string[]; limit?: number; merges?: boolean; ref?: string; reverse?: boolean },
+ options: {
+ authors?: string[];
+ limit?: number;
+ merges?: boolean;
+ ordering?: string | null;
+ ref?: string;
+ reverse?: boolean;
+ },
): (limit: number | { until: string } | undefined) => Promise {
return async (limit: number | { until: string } | undefined) => {
const moreUntil = limit != null && typeof limit === 'object' ? limit.until : undefined;
@@ -2021,7 +2033,7 @@ export class GitService implements Disposable {
async getLogForSearch(
repoPath: string,
search: SearchPattern,
- options: { limit?: number; skip?: number } = {},
+ options: { limit?: number; ordering?: string | null; skip?: number } = {},
): Promise {
search = { matchAll: false, matchCase: false, matchRegex: true, ...search };
@@ -2098,7 +2110,12 @@ export class GitService implements Disposable {
args.push(...files);
}
- const data = await Git.log__search(repoPath, args, { ...options, limit: limit, useShow: useShow });
+ const data = await Git.log__search(repoPath, args, {
+ ordering: Container.config.advanced.commitOrdering,
+ ...options,
+ limit: limit,
+ useShow: useShow,
+ });
const log = GitLogParser.parse(
data,
GitCommitType.Log,
@@ -2128,7 +2145,7 @@ export class GitService implements Disposable {
private getLogForSearchMoreFn(
log: GitLog,
search: SearchPattern,
- options: { limit?: number },
+ options: { limit?: number; ordering?: string | null },
): (limit: number | undefined) => Promise {
return async (limit: number | undefined) => {
limit = limit ?? Container.config.advanced.maxSearchItems ?? 0;
@@ -2180,6 +2197,7 @@ export class GitService implements Disposable {
options: {
all?: boolean;
limit?: number;
+ ordering?: string | null;
range?: Range;
ref?: string;
renames?: boolean;
@@ -2330,6 +2348,7 @@ export class GitService implements Disposable {
}: {
all?: boolean;
limit?: number;
+ ordering?: string | null;
range?: Range;
ref?: string;
renames?: boolean;
@@ -2354,6 +2373,7 @@ export class GitService implements Disposable {
}
const data = await Git.log__file(root, file, ref, {
+ ordering: Container.config.advanced.commitOrdering,
...options,
firstParent: options.renames,
startLine: range == null ? undefined : range.start.line + 1,
@@ -2404,7 +2424,15 @@ export class GitService implements Disposable {
private getLogForFileMoreFn(
log: GitLog,
fileName: string,
- options: { all?: boolean; limit?: number; range?: Range; ref?: string; renames?: boolean; reverse?: boolean },
+ options: {
+ all?: boolean;
+ limit?: number;
+ ordering?: string | null;
+ range?: Range;
+ ref?: string;
+ renames?: boolean;
+ reverse?: boolean;
+ },
): (limit: number | { until: string } | undefined) => Promise {
return async (limit: number | { until: string } | undefined) => {
const moreUntil = limit != null && typeof limit === 'object' ? limit.until : undefined;
@@ -2669,10 +2697,11 @@ export class GitService implements Disposable {
const fileName = GitUri.relativeTo(uri, repoPath);
let data = await Git.log__file(repoPath, fileName, ref, {
filters: filters,
+ format: 'simple',
limit: skip + 1,
- // startLine: editorLine != null ? editorLine + 1 : undefined,
+ ordering: Container.config.advanced.commitOrdering,
reverse: true,
- format: 'simple',
+ // startLine: editorLine != null ? editorLine + 1 : undefined,
});
if (data == null || data.length === 0) return undefined;
@@ -2681,9 +2710,10 @@ export class GitService implements Disposable {
if (status === 'D') {
data = await Git.log__file(repoPath, '.', nextRef, {
filters: ['R', 'C'],
+ format: 'simple',
limit: 1,
+ ordering: Container.config.advanced.commitOrdering,
// startLine: editorLine != null ? editorLine + 1 : undefined
- format: 'simple',
});
if (data == null || data.length === 0) {
return GitUri.fromFile(file ?? fileName, repoPath, nextRef);
@@ -2918,9 +2948,10 @@ export class GitService implements Disposable {
let data;
try {
data = await Git.log__file(repoPath, fileName, ref, {
- limit: skip + 2,
firstParent: firstParent,
format: 'simple',
+ limit: skip + 2,
+ ordering: Container.config.advanced.commitOrdering,
startLine: editorLine != null ? editorLine + 1 : undefined,
});
} catch (ex) {
@@ -2934,7 +2965,9 @@ export class GitService implements Disposable {
}
}
- ref = await Git.log__file_recent(repoPath, fileName);
+ ref = await Git.log__file_recent(repoPath, fileName, {
+ ordering: Container.config.advanced.commitOrdering,
+ });
return GitUri.fromFile(fileName, repoPath, ref ?? GitRevision.deletedOrMissing);
}
@@ -3056,14 +3089,21 @@ export class GitService implements Disposable {
@log()
async getIncomingActivity(
repoPath: string,
- { limit, ...options }: { all?: boolean; branch?: string; limit?: number; skip?: number } = {},
+ {
+ limit,
+ ...options
+ }: { all?: boolean; branch?: string; limit?: number; ordering?: string | null; skip?: number } = {},
): Promise {
const cc = Logger.getCorrelationContext();
limit = limit ?? Container.config.advanced.maxListItems ?? 0;
try {
// 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 });
+ const data = await Git.reflog(repoPath, {
+ ordering: Container.config.advanced.commitOrdering,
+ ...options,
+ limit: limit * 100,
+ });
if (data == null) return undefined;
const reflog = GitReflogParser.parse(data, repoPath, reflogCommands, limit, limit * 100);
@@ -3080,7 +3120,7 @@ export class GitService implements Disposable {
private getReflogMoreFn(
reflog: GitReflog,
- options: { all?: boolean; branch?: string; limit?: number; skip?: number },
+ options: { all?: boolean; branch?: string; limit?: number; ordering?: string | null; skip?: number },
): (limit: number) => Promise {
return async (limit: number | undefined) => {
limit = limit ?? Container.config.advanced.maxSearchItems ?? 0;
@@ -3721,6 +3761,7 @@ export class GitService implements Disposable {
// TODO: Add caching
// Get the most recent commit for this file name
ref = await Git.log__file_recent(repoPath, fileName, {
+ ordering: Container.config.advanced.commitOrdering,
similarityThreshold: Container.config.advanced.similarityThreshold,
});
if (ref == null) return undefined;
@@ -3728,8 +3769,9 @@ export class GitService implements Disposable {
// Now check if that commit had any renames
data = await Git.log__file(repoPath, '.', ref, {
filters: ['R', 'C', 'D'],
- limit: 1,
format: 'simple',
+ limit: 1,
+ ordering: Container.config.advanced.commitOrdering,
});
if (data == null || data.length === 0) break;
@@ -4012,7 +4054,13 @@ export class GitService implements Disposable {
const blob = await Git.rev_parse__verify(repoPath, ref, fileName);
if (blob == null) return GitRevision.deletedOrMissing;
- let promise: Promise = Git.log__find_object(repoPath, blob, ref, fileName);
+ let promise: Promise = Git.log__find_object(
+ repoPath,
+ blob,
+ ref,
+ Container.config.advanced.commitOrdering,
+ fileName,
+ );
if (options?.timeout != null) {
promise = Promise.race([promise, Functions.wait(options.timeout)]);
}