Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix 645: add reverse order #647

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ media
node_modules
out
*.vsix
yarn.lock
package-lock.json
9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "git-graph",
"displayName": "Git Graph",
"version": "1.30.0",
"version": "1.30.1",
"publisher": "mhutchie",
"author": {
"name": "Michael Hutchison",
Expand Down Expand Up @@ -1072,6 +1072,11 @@
"default": true,
"description": "Show Commits that are only referenced by tags in Git Graph."
},
"git-graph.repository.showReverseCommits": {
"type": "boolean",
"default": false,
"description": "Show Commits in reverse order by default. This can be overridden per repository from the Git Graph View's Control Bar."
},
"git-graph.repository.showRemoteBranches": {
"type": "boolean",
"default": true,
Expand Down Expand Up @@ -1515,7 +1520,7 @@
"vscode:prepublish": "npm run compile",
"vscode:uninstall": "node ./out/life-cycle/uninstall.js",
"clean": "node ./.vscode/clean.js",
"compile": "npm run lint && npm run clean && npm run compile-src && npm run compile-web",
"compile": "npm run lint && npm run clean && npm run compile-src && npm run compile-web-debug",
"compile-src": "tsc -p ./src && node ./.vscode/package-src.js",
"compile-web": "tsc -p ./web && node ./.vscode/package-web.js",
"compile-web-debug": "tsc -p ./web && node ./.vscode/package-web.js debug",
Expand Down
7 changes: 7 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,13 @@ class Config {
return !!this.getRenamedExtensionSetting('repository.showCommitsOnlyReferencedByTags', 'showCommitsOnlyReferencedByTags', true);
}

/**
* Get the value of the `git-graph.repository.showReverseCommits` Extension Setting.
*/
get showReverseCommits() {
return !!this.config.get('repository.showReverseCommits', false);
}

/**
* Get the value of the `git-graph.repository.showRemoteBranches` Extension Setting.
*/
Expand Down
212 changes: 117 additions & 95 deletions src/dataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { Disposable } from './utils/disposable';
import { Event } from './utils/event';

const DRIVE_LETTER_PATH_REGEX = /^[a-z]:\//;
const EOL_REGEX = /\r\n|\r|\n/g;
const EOL_REGEX = /\r\n|\n/g;
const INVALID_BRANCH_REGEXP = /^\(.* .*\)$/;
const REMOTE_HEAD_BRANCH_REGEXP = /^remotes\/.*\/HEAD$/;
const GIT_LOG_SEPARATOR = 'XX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPb';
Expand Down Expand Up @@ -161,114 +161,123 @@ export class DataSource extends Disposable {
* @param stashes An array of all stashes in the repository.
* @returns The commits in the repository.
*/
public getCommits(repo: string, branches: ReadonlyArray<string> | null, maxCommits: number, showTags: boolean, showRemoteBranches: boolean, includeCommitsMentionedByReflogs: boolean, onlyFollowFirstParent: boolean, commitOrdering: CommitOrdering, remotes: ReadonlyArray<string>, hideRemotes: ReadonlyArray<string>, stashes: ReadonlyArray<GitStash>): Promise<GitCommitData> {
public getCommits(repo: string, branches: ReadonlyArray<string> | null, maxCommits: number, showTags: boolean, showReverseCommits: boolean, showRemoteBranches: boolean, includeCommitsMentionedByReflogs: boolean, onlyFollowFirstParent: boolean, commitOrdering: CommitOrdering, remotes: ReadonlyArray<string>, hideRemotes: ReadonlyArray<string>, stashes: ReadonlyArray<GitStash>): Promise<GitCommitData> {
const config = getConfig();
return Promise.all([
this.getLog(repo, branches, maxCommits + 1, showTags && config.showCommitsOnlyReferencedByTags, showRemoteBranches, includeCommitsMentionedByReflogs, onlyFollowFirstParent, commitOrdering, remotes, hideRemotes, stashes),
this.getRefs(repo, showRemoteBranches, config.showRemoteHeads, hideRemotes).then((refData: GitRefData) => refData, (errorMessage: string) => errorMessage)
]).then(async (results) => {
let commits: GitCommitRecord[] = results[0], refData: GitRefData | string = results[1], i;
let moreCommitsAvailable = commits.length === maxCommits + 1;
if (moreCommitsAvailable) commits.pop();

// It doesn't matter if getRefs() was rejected if no commits exist
if (typeof refData === 'string') {
// getRefs() returned an error message (string)
if (commits.length > 0) {
// Commits exist, throw the error
throw refData;
} else {
// No commits exist, so getRefs() will always return an error. Set refData to the default value
refData = { head: null, heads: [], tags: [], remotes: [] };

return this.getCommitsCount(repo).then((count) => {
return Promise.all([
this.getLog(repo, branches, maxCommits + 1, count, showTags && config.showCommitsOnlyReferencedByTags, showReverseCommits, showRemoteBranches, includeCommitsMentionedByReflogs, onlyFollowFirstParent, commitOrdering, remotes, hideRemotes, stashes),
this.getRefs(repo, showRemoteBranches, config.showRemoteHeads, hideRemotes).then((refData: GitRefData) => refData, (errorMessage: string) => errorMessage)
]).then(async (results) => {
let commits: GitCommitRecord[] = results[0], refData: GitRefData | string = results[1], i;
let moreCommitsAvailable = commits.length === maxCommits + 1;
if (moreCommitsAvailable) commits.pop();

// It doesn't matter if getRefs() was rejected if no commits exist
if (typeof refData === 'string') {
// getRefs() returned an error message (string)
if (commits.length > 0) {
// Commits exist, throw the error
throw refData;
} else {
// No commits exist, so getRefs() will always return an error. Set refData to the default value
refData = { head: null, heads: [], tags: [], remotes: [] };
}
}
}

if (refData.head !== null && config.showUncommittedChanges) {
for (i = 0; i < commits.length; i++) {
if (refData.head === commits[i].hash) {
const numUncommittedChanges = await this.getUncommittedChanges(repo);
if (numUncommittedChanges > 0) {
commits.unshift({ hash: UNCOMMITTED, parents: [refData.head], author: '*', email: '', date: Math.round((new Date()).getTime() / 1000), message: 'Uncommitted Changes (' + numUncommittedChanges + ')' });
if (refData.head !== null && config.showUncommittedChanges) {
for (i = 0; i < commits.length; i++) {
if (refData.head === commits[i].hash) {
const numUncommittedChanges = await this.getUncommittedChanges(repo);
if (numUncommittedChanges > 0) {
const cn = { hash: UNCOMMITTED, parents: [refData.head], author: '*', email: '', date: Math.round((new Date()).getTime() / 1000), message: 'Uncommitted Changes (' + numUncommittedChanges + ')' };
if (showReverseCommits) {
commits.push(cn);
} else {
commits.unshift(cn);
}
}
break;
}
break;
}
}
}

let commitNodes: DeepWriteable<GitCommit>[] = [];
let commitLookup: { [hash: string]: number } = {};
let commitNodes: DeepWriteable<GitCommit>[] = [];
let commitLookup: { [hash: string]: number } = {};

for (i = 0; i < commits.length; i++) {
commitLookup[commits[i].hash] = i;
commitNodes.push({ ...commits[i], heads: [], tags: [], remotes: [], stash: null });
}

/* Insert Stashes */
let toAdd: { index: number, data: GitStash }[] = [];
for (i = 0; i < stashes.length; i++) {
if (typeof commitLookup[stashes[i].hash] === 'number') {
commitNodes[commitLookup[stashes[i].hash]].stash = {
selector: stashes[i].selector,
baseHash: stashes[i].baseHash,
untrackedFilesHash: stashes[i].untrackedFilesHash
};
} else if (typeof commitLookup[stashes[i].baseHash] === 'number') {
toAdd.push({ index: commitLookup[stashes[i].baseHash], data: stashes[i] });
for (i = 0; i < commits.length; i++) {
commitLookup[commits[i].hash] = i;
commitNodes.push({ ...commits[i], heads: [], tags: [], remotes: [], stash: null });
}
}
toAdd.sort((a, b) => a.index !== b.index ? a.index - b.index : b.data.date - a.data.date);
for (i = toAdd.length - 1; i >= 0; i--) {
let stash = toAdd[i].data;
commitNodes.splice(toAdd[i].index, 0, {
hash: stash.hash,
parents: [stash.baseHash],
author: stash.author,
email: stash.email,
date: stash.date,
message: stash.message,
heads: [], tags: [], remotes: [],
stash: {
selector: stash.selector,
baseHash: stash.baseHash,
untrackedFilesHash: stash.untrackedFilesHash

/* Insert Stashes */
// OK for reverse order?
let toAdd: { index: number, data: GitStash }[] = [];
for (i = 0; i < stashes.length; i++) {
if (typeof commitLookup[stashes[i].hash] === 'number') {
commitNodes[commitLookup[stashes[i].hash]].stash = {
selector: stashes[i].selector,
baseHash: stashes[i].baseHash,
untrackedFilesHash: stashes[i].untrackedFilesHash
};
} else if (typeof commitLookup[stashes[i].baseHash] === 'number') {
toAdd.push({ index: commitLookup[stashes[i].baseHash], data: stashes[i] });
}
});
}
for (i = 0; i < commitNodes.length; i++) {
// Correct commit lookup after stashes have been spliced in
commitLookup[commitNodes[i].hash] = i;
}
}
toAdd.sort((a, b) => a.index !== b.index ? a.index - b.index : b.data.date - a.data.date);
for (i = toAdd.length - 1; i >= 0; i--) {
let stash = toAdd[i].data;
commitNodes.splice(toAdd[i].index, 0, {
hash: stash.hash,
parents: [stash.baseHash],
author: stash.author,
email: stash.email,
date: stash.date,
message: stash.message,
heads: [], tags: [], remotes: [],
stash: {
selector: stash.selector,
baseHash: stash.baseHash,
untrackedFilesHash: stash.untrackedFilesHash
}
});
}
for (i = 0; i < commitNodes.length; i++) {
// Correct commit lookup after stashes have been spliced in
commitLookup[commitNodes[i].hash] = i;
}

/* Annotate Heads */
for (i = 0; i < refData.heads.length; i++) {
if (typeof commitLookup[refData.heads[i].hash] === 'number') commitNodes[commitLookup[refData.heads[i].hash]].heads.push(refData.heads[i].name);
}
/* Annotate Heads */
for (i = 0; i < refData.heads.length; i++) {
if (typeof commitLookup[refData.heads[i].hash] === 'number') commitNodes[commitLookup[refData.heads[i].hash]].heads.push(refData.heads[i].name);
}

/* Annotate Tags */
if (showTags) {
for (i = 0; i < refData.tags.length; i++) {
if (typeof commitLookup[refData.tags[i].hash] === 'number') commitNodes[commitLookup[refData.tags[i].hash]].tags.push({ name: refData.tags[i].name, annotated: refData.tags[i].annotated });
/* Annotate Tags */
if (showTags) {
for (i = 0; i < refData.tags.length; i++) {
if (typeof commitLookup[refData.tags[i].hash] === 'number') commitNodes[commitLookup[refData.tags[i].hash]].tags.push({ name: refData.tags[i].name, annotated: refData.tags[i].annotated });
}
}
}

/* Annotate Remotes */
for (i = 0; i < refData.remotes.length; i++) {
if (typeof commitLookup[refData.remotes[i].hash] === 'number') {
let name = refData.remotes[i].name;
let remote = remotes.find(remote => name.startsWith(remote + '/'));
commitNodes[commitLookup[refData.remotes[i].hash]].remotes.push({ name: name, remote: remote ? remote : null });
/* Annotate Remotes */
for (i = 0; i < refData.remotes.length; i++) {
if (typeof commitLookup[refData.remotes[i].hash] === 'number') {
let name = refData.remotes[i].name;
let remote = remotes.find(remote => name.startsWith(remote + '/'));
commitNodes[commitLookup[refData.remotes[i].hash]].remotes.push({ name: name, remote: remote ? remote : null });
}
}
}

return {
commits: commitNodes,
head: refData.head,
tags: unique(refData.tags.map((tag) => tag.name)),
moreCommitsAvailable: moreCommitsAvailable,
error: null
};
}).catch((errorMessage) => {
return { commits: [], head: null, tags: [], moreCommitsAvailable: false, error: errorMessage };
return {
commits: commitNodes,
head: refData.head,
tags: unique(refData.tags.map((tag) => tag.name)),
moreCommitsAvailable: moreCommitsAvailable,
error: null
};
}).catch((errorMessage) => {
return { commits: [], head: null, tags: [], moreCommitsAvailable: false, error: errorMessage };
});
});
}

Expand Down Expand Up @@ -452,6 +461,11 @@ export class DataSource extends Disposable {

/* Get Data Methods - General */

public getCommitsCount(repo: string): Promise<number> {
return this.spawnGit(['rev-list', '--all', '--count'], repo, (stdout) => {
return Number.parseInt(stdout);
}).then((count) => count, () => 0);
}
/**
* Get the subject of a commit.
* @param repo The path of the repository.
Expand Down Expand Up @@ -1496,8 +1510,16 @@ export class DataSource extends Disposable {
* @param stashes An array of all stashes in the repository.
* @returns An array of commits.
*/
private getLog(repo: string, branches: ReadonlyArray<string> | null, num: number, includeTags: boolean, includeRemotes: boolean, includeCommitsMentionedByReflogs: boolean, onlyFollowFirstParent: boolean, order: CommitOrdering, remotes: ReadonlyArray<string>, hideRemotes: ReadonlyArray<string>, stashes: ReadonlyArray<GitStash>) {
const args = ['-c', 'log.showSignature=false', 'log', '--max-count=' + num, '--format=' + this.gitFormatLog, '--' + order + '-order'];
private getLog(repo: string, branches: ReadonlyArray<string> | null, num: number, allcount: number, includeTags: boolean, showReverseCommits: boolean, includeRemotes: boolean, includeCommitsMentionedByReflogs: boolean, onlyFollowFirstParent: boolean, order: CommitOrdering, remotes: ReadonlyArray<string>, hideRemotes: ReadonlyArray<string>, stashes: ReadonlyArray<GitStash>) {
let args = [];
if (showReverseCommits) {
let skipnum = allcount - num;
if (skipnum <= 0) skipnum = 0;
args = ['-c', 'log.showSignature=false', 'log', '--skip=' + skipnum, '--format=' + this.gitFormatLog, '--date-order', '--reverse'];
} else {
args = ['-c', 'log.showSignature=false', 'log', '--max-count=' + num, '--format=' + this.gitFormatLog, '--' + order + '-order'];
}
// const args = ['-c', 'log.showSignature=false', 'log', '--max-count=' + num, '--format=' + this.gitFormatLog, '--' + order + '-order'];
if (onlyFollowFirstParent) {
args.push('--first-parent');
}
Expand Down
1 change: 1 addition & 0 deletions src/extensionState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const DEFAULT_REPO_STATE: GitRepoState = {
onRepoLoadShowCheckedOutBranch: BooleanOverride.Default,
onRepoLoadShowSpecificBranches: null,
pullRequestConfig: null,
showReverseCommits: false,
showRemoteBranches: true,
showRemoteBranchesV2: BooleanOverride.Default,
showStashes: BooleanOverride.Default,
Expand Down
5 changes: 4 additions & 1 deletion src/gitGraphView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,8 @@ export class GitGraphView extends Disposable {
command: 'loadCommits',
refreshId: msg.refreshId,
onlyFollowFirstParent: msg.onlyFollowFirstParent,
...await this.dataSource.getCommits(msg.repo, msg.branches, msg.maxCommits, msg.showTags, msg.showRemoteBranches, msg.includeCommitsMentionedByReflogs, msg.onlyFollowFirstParent, msg.commitOrdering, msg.remotes, msg.hideRemotes, msg.stashes)
showReverseCommits: msg.showReverseCommits,
...await this.dataSource.getCommits(msg.repo, msg.branches, msg.maxCommits, msg.showTags, msg.showReverseCommits, msg.showRemoteBranches, msg.includeCommitsMentionedByReflogs, msg.onlyFollowFirstParent, msg.commitOrdering, msg.remotes, msg.hideRemotes, msg.stashes)
});
break;
case 'loadConfig':
Expand Down Expand Up @@ -690,6 +691,7 @@ export class GitGraphView extends Disposable {
onRepoLoad: config.onRepoLoad,
referenceLabels: config.referenceLabels,
repoDropdownOrder: config.repoDropdownOrder,
showReverseCommits: config.showReverseCommits,
showRemoteBranches: config.showRemoteBranches,
showStashes: config.showStashes,
showTags: config.showTags
Expand Down Expand Up @@ -720,6 +722,7 @@ export class GitGraphView extends Disposable {
<div id="controls">
<span id="repoControl"><span class="unselectable">Repo: </span><div id="repoDropdown" class="dropdown"></div></span>
<span id="branchControl"><span class="unselectable">Branches: </span><div id="branchDropdown" class="dropdown"></div></span>
<label id="showReverseCommitsControl"><input type="checkbox" id="showReverseCommitsCheckbox" tabindex="-1"><span class="customCheckbox"></span>Reverse</label>
<label id="showRemoteBranchesControl"><input type="checkbox" id="showRemoteBranchesCheckbox" tabindex="-1"><span class="customCheckbox"></span>Show Remote Branches</label>
<div id="findBtn" title="Find"></div>
<div id="terminalBtn" title="Open a Terminal for this Repository"></div>
Expand Down
5 changes: 5 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* Git Interfaces / Types */


export interface GitCommit {
readonly hash: string;
readonly parents: ReadonlyArray<string>;
Expand Down Expand Up @@ -212,6 +213,7 @@ export interface GitRepoState {
onRepoLoadShowCheckedOutBranch: BooleanOverride;
onRepoLoadShowSpecificBranches: string[] | null;
pullRequestConfig: PullRequestConfig | null;
showReverseCommits: boolean;
showRemoteBranches: boolean;
showRemoteBranchesV2: BooleanOverride;
showStashes: BooleanOverride;
Expand Down Expand Up @@ -257,6 +259,7 @@ export interface GitGraphViewConfig {
readonly onRepoLoad: OnRepoLoadConfig;
readonly referenceLabels: ReferenceLabelsConfig;
readonly repoDropdownOrder: RepoDropdownOrder;
readonly showReverseCommits: boolean;
readonly showRemoteBranches: boolean;
readonly showStashes: boolean;
readonly showTags: boolean;
Expand Down Expand Up @@ -904,6 +907,7 @@ export interface RequestLoadCommits extends RepoRequest {
readonly branches: ReadonlyArray<string> | null; // null => Show All
readonly maxCommits: number;
readonly showTags: boolean;
readonly showReverseCommits: boolean;
readonly showRemoteBranches: boolean;
readonly includeCommitsMentionedByReflogs: boolean;
readonly onlyFollowFirstParent: boolean;
Expand All @@ -920,6 +924,7 @@ export interface ResponseLoadCommits extends ResponseWithErrorInfo {
readonly tags: string[];
readonly moreCommitsAvailable: boolean;
readonly onlyFollowFirstParent: boolean;
readonly showReverseCommits: boolean;
}

export interface RequestLoadConfig extends RepoRequest {
Expand Down
Loading