Browse Source

Closes #1257 - Adds commit ordering option

Co-authored-by: Shashank-Shastri <shashank.m.shastri@gmail.com>
Co-authored-by: Eric Amodio <eamodio@gmail.com>
main
Eric Amodio 3 years ago
parent
commit
ecbf4cdfcb
5 changed files with 170 additions and 32 deletions
  1. +1
    -0
      README.md
  2. +21
    -0
      package.json
  3. +1
    -0
      src/config.ts
  4. +80
    -13
      src/git/git.ts
  5. +67
    -19
      src/git/gitService.ts

+ 1
- 0
README.md View File

@ -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 &mdash; 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<br /><br />`date` - shows commits in reverse chronological order of the commit timestamp<br />`author-date` - shows commits in reverse chronological order of the author timestamp<br />`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 |

+ 21
- 0
package.json View File

@ -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",

+ 1
- 0
src/config.ts View File

@ -308,6 +308,7 @@ export interface AdvancedConfig {
caching: {
enabled: boolean;
};
commitOrdering: string | null;
externalDiffTool: string | null;
externalDirectoryDiffTool: string | null;
fileHistoryFollowsRenames: boolean;

+ 80
- 13
src/git/git.ts View File

@ -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<string>(
{ 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<string>(
{ 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<string>(
{ 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<string> {
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<string>(
@ -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];

+ 67
- 19
src/git/gitService.ts View File

@ -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<string | undefined> {
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<GitLog> {
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<GitLog | undefined> {
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<GitLog> {
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<GitLog> {
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<GitReflog | undefined> {
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<GitReflog> {
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<string | void | undefined> = Git.log__find_object(repoPath, blob, ref, fileName);
let promise: Promise<string | void | undefined> = Git.log__find_object(
repoPath,
blob,
ref,
Container.config.advanced.commitOrdering,
fileName,
);
if (options?.timeout != null) {
promise = Promise.race([promise, Functions.wait(options.timeout)]);
}

Loading…
Cancel
Save