Skip to content

Commit

Permalink
Separates branch comparison into ahead/behind
Browse files Browse the repository at this point in the history
  • Loading branch information
eamodio committed Oct 13, 2020
1 parent e2ecc34 commit 143d55b
Show file tree
Hide file tree
Showing 11 changed files with 205 additions and 145 deletions.
11 changes: 4 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3604,18 +3604,15 @@
},
{
"command": "gitlens.views.setBranchComparisonToWorking",
"title": "Toggle Comparison Source (Working Tree)",
"title": "Toggle Source (Branch)",
"category": "GitLens",
"icon": {
"dark": "images/dark/icon-compare-ref-working.svg",
"light": "images/light/icon-compare-ref-working.svg"
}
"icon": "$(git-branch)"
},
{
"command": "gitlens.views.setBranchComparisonToBranch",
"title": "Toggle Comparison Source (Branch)",
"title": "Toggle Source (Working Tree)",
"category": "GitLens",
"icon": "$(compare-changes)"
"icon": "$(list-tree)"
},
{
"command": "gitlens.views.clearNode",
Expand Down
8 changes: 4 additions & 4 deletions src/commands/git/merge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,10 @@ export class MergeGitCommand extends QuickCommand<State> {
}

private async *confirmStep(state: MergeStepState, context: Context): StepResultGenerator<Flags[]> {
const count =
(await Container.git.getCommitCount(state.repo.path, [
GitRevision.createRange(context.destination.name, state.reference.name),
])) ?? 0;
const aheadBehind = await Container.git.getAheadBehindCommitCount(state.repo.path, [
GitRevision.createRange(context.destination.name, state.reference.name),
]);
const count = aheadBehind != null ? aheadBehind.ahead + aheadBehind.behind : 0;
if (count === 0) {
const step: QuickPickStep<DirectiveQuickPickItem> = this.createConfirmStep(
appendReposToTitle(`Confirm ${context.title}`, state, context),
Expand Down
8 changes: 4 additions & 4 deletions src/commands/git/rebase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,10 @@ export class RebaseGitCommand extends QuickCommand<State> {
}

private async *confirmStep(state: RebaseStepState, context: Context): StepResultGenerator<Flags[]> {
const count =
(await Container.git.getCommitCount(state.repo.path, [
GitRevision.createRange(state.reference.ref, context.destination.ref),
])) ?? 0;
const aheadBehind = await Container.git.getAheadBehindCommitCount(state.repo.path, [
GitRevision.createRange(context.destination.name, state.reference.name),
]);
const count = aheadBehind != null ? aheadBehind.ahead + aheadBehind.behind : 0;
if (count === 0) {
const step: QuickPickStep<DirectiveQuickPickItem> = this.createConfirmStep(
appendReposToTitle(`Confirm ${context.title}`, state, context),
Expand Down
31 changes: 22 additions & 9 deletions src/git/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1017,16 +1017,29 @@ export namespace Git {
export async function rev_list(
repoPath: string,
refs: string[],
options: { count?: boolean } = {},
): Promise<number | undefined> {
const params = [];
if (options.count) {
params.push('--count');
}
params.push(...refs);
): Promise<{ ahead: number; behind: number } | undefined> {
const data = await git<string>(
{ cwd: repoPath, errors: GitErrorHandling.Ignore },
'rev-list',
'--left-right',
'--count',
...refs,
'--',
);
if (data.length === 0) return undefined;

const parts = data.split('\t');
if (parts.length !== 2) return undefined;

const [ahead, behind] = parts;
const result = {
ahead: parseInt(ahead, 10),
behind: parseInt(behind, 10),
};

if (isNaN(result.ahead) || isNaN(result.behind)) return undefined;

const data = await git<string>({ cwd: repoPath, errors: GitErrorHandling.Ignore }, 'rev-list', ...params, '--');
return data.length === 0 ? undefined : Number(data.trim()) || undefined;
return result;
}

export async function rev_parse__currentBranch(
Expand Down
7 changes: 5 additions & 2 deletions src/git/gitService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1260,8 +1260,11 @@ export class GitService implements Disposable {
}

@log()
getCommitCount(repoPath: string, refs: string[]) {
return Git.rev_list(repoPath, refs, { count: true });
getAheadBehindCommitCount(
repoPath: string,
refs: string[],
): Promise<{ ahead: number; behind: number } | undefined> {
return Git.rev_list(repoPath, refs);
}

@log()
Expand Down
194 changes: 103 additions & 91 deletions src/views/nodes/compareBranchNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { CommandQuickPickItem, ReferencePicker } from '../../quickpicks';
import { RepositoriesView } from '../repositoriesView';
import { RepositoryNode } from './repositoryNode';
import { CommitsQueryResults, ResultsCommitsNode } from './resultsCommitsNode';
import { FilesQueryResults, ResultsFilesNode } from './resultsFilesNode';
import { FilesQueryResults } from './resultsFilesNode';
import { debug, gate, log, Strings } from '../../system';
import { ContextValues, ViewNode } from './viewNode';

Expand All @@ -37,7 +37,7 @@ export class CompareBranchNode extends ViewNode<BranchesView | CommitsView | Rep
if (compareWith !== undefined && typeof compareWith === 'string') {
this._compareWith = {
ref: compareWith,
notation: Container.config.advanced.useSymmetricDifferenceNotation ? '...' : '..',
notation: undefined,
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
type: this.view.config.showBranchComparison || ViewShowBranchComparison.Working,
};
Expand All @@ -51,33 +51,57 @@ export class CompareBranchNode extends ViewNode<BranchesView | CommitsView | Rep
}

async getChildren(): Promise<ViewNode[]> {
if (this._compareWith === undefined) return [];
if (this._compareWith == null) return [];

if (this._children === undefined) {
let ref1 = this._compareWith.ref || 'HEAD';
if (this.comparisonNotation === '..') {
ref1 = (await Container.git.getMergeBase(this.branch.repoPath, ref1, this.branch.ref)) ?? ref1;
}
if (this._children == null) {
const aheadBehind = await Container.git.getAheadBehindCommitCount(this.branch.repoPath, [
GitRevision.createRange(this.branch.ref || 'HEAD', this._compareWith.ref || 'HEAD', '...'),
]);

this._children = [
new ResultsCommitsNode(
this.view,
this,
this.uri.repoPath!,
'commits',
this.getCommitsQuery.bind(this),
'Behind', //`Behind (${aheadBehind?.behind})`,
this.getCommitsQuery(
GitRevision.createRange(this.branch.ref, this._compareWith?.ref ?? 'HEAD', '..'),
),
{
id: 'behind',
description: Strings.pluralize('commit', aheadBehind?.behind ?? 0),
expand: false,
includeDescription: false,
includeRepoName: true,
files: {
ref1: this.branch.ref,
ref2: this._compareWith.ref || 'HEAD',
query: this.getBehindFilesQuery.bind(this),
},
},
),
new ResultsFilesNode(
new ResultsCommitsNode(
this.view,
this,
this.uri.repoPath!,
ref1,
this.compareWithWorkingTree ? '' : this.branch.ref,
this.getFilesQuery.bind(this),
'Ahead', //`Ahead (${aheadBehind?.ahead})`,
this.getCommitsQuery(
GitRevision.createRange(
this._compareWith?.ref ?? 'HEAD',
this.compareWithWorkingTree ? '' : this.branch.ref,
'..',
),
),
{
id: 'ahead',
description: Strings.pluralize('commit', aheadBehind?.ahead ?? 0),
expand: false,
includeRepoName: true,
files: {
ref1: this._compareWith.ref || 'HEAD',
ref2: this.compareWithWorkingTree ? '' : this.branch.ref,
query: this.getAheadFilesQuery.bind(this),
},
},
),
];
}
Expand Down Expand Up @@ -109,18 +133,11 @@ export class CompareBranchNode extends ViewNode<BranchesView | CommitsView | Rep
command: 'gitlens.views.executeNodeCallback',
arguments: [() => this.compareWith()],
};
item.contextValue = `${ContextValues.CompareBranch}${this.branch.current ? '+current' : ''}${
this._compareWith === undefined ? '' : '+comparing'
}+${this.comparisonNotation === '..' ? 'twodot' : 'threedot'}+${this.comparisonType}`;
item.contextValue = `${ContextValues.CompareBranch}${this.branch.current ? '+current' : ''}+${
this.comparisonType
}${this._compareWith == null ? '' : '+comparing'}`;
item.description = description;
if (this.compareWithWorkingTree) {
item.iconPath = {
dark: Container.context.asAbsolutePath('images/dark/icon-compare-ref-working.svg'),
light: Container.context.asAbsolutePath('images/light/icon-compare-ref-working.svg'),
};
} else {
item.iconPath = new ThemeIcon('git-compare');
}
item.iconPath = new ThemeIcon('git-compare');
item.id = this.id;
item.tooltip = `Click to compare ${this.branch.name}${this.compareWithWorkingTree ? ' (working)' : ''} with${
GlyphChars.Ellipsis
Expand All @@ -140,14 +157,10 @@ export class CompareBranchNode extends ViewNode<BranchesView | CommitsView | Rep
this.view.triggerNodeChange(this);
}

@log()
async setComparisonNotation(comparisonNotation: '...' | '..') {
if (this._compareWith !== undefined) {
await this.updateCompareWith({ ...this._compareWith, notation: comparisonNotation });
}

@gate()
@debug()
refresh() {
this._children = undefined;
this.view.triggerNodeChange(this);
}

@log()
Expand All @@ -160,17 +173,6 @@ export class CompareBranchNode extends ViewNode<BranchesView | CommitsView | Rep
this.view.triggerNodeChange(this);
}

private get comparisonNotation(): '..' | '...' {
return this._compareWith?.notation ?? (Container.config.advanced.useSymmetricDifferenceNotation ? '...' : '..');
}

private get diffComparisonNotation(): '..' | '...' {
// In git diff the range syntax doesn't mean the same thing as with git log -- since git diff is about comparing endpoints not ranges
// see https://git-scm.com/docs/git-diff#Documentation/git-diff.txt-emgitdiffemltoptionsgtltcommitgtltcommitgt--ltpathgt82308203
// So inverting the range syntax should be about equivalent for the behavior we want
return this.comparisonNotation === '...' ? '..' : '...';
}

private get comparisonType() {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
return this._compareWith?.type ?? (this.view.config.showBranchComparison || ViewShowBranchComparison.Working);
Expand Down Expand Up @@ -199,68 +201,76 @@ export class CompareBranchNode extends ViewNode<BranchesView | CommitsView | Rep

await this.updateCompareWith({
ref: pick.ref,
notation: this.comparisonNotation,
notation: undefined,
type: this.comparisonType,
});

this._children = undefined;
this.view.triggerNodeChange(this);
}

private async getCommitsQuery(limit: number | undefined): Promise<CommitsQueryResults> {
const log = await Container.git.getLog(this.uri.repoPath!, {
limit: limit,
ref: GitRevision.createRange(
this._compareWith?.ref ?? 'HEAD',
this.compareWithWorkingTree ? '' : this.branch.ref,
this.comparisonNotation,
),
});

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;
private getCommitsQuery(range: string): (limit: number | undefined) => Promise<CommitsQueryResults> {
const repoPath = this.uri.repoPath!;
return async (limit: number | undefined) => {
const log = await Container.git.getLog(repoPath, {
limit: limit,
ref: range,
});

const results: Mutable<Partial<CommitsQueryResults>> = {
log: log,
hasMore: log?.hasMore ?? true,
};
}

return results as CommitsQueryResults;
}
if (results.hasMore) {
results.more = async (limit: number | undefined) => {
results.log = (await results.log?.more?.(limit)) ?? results.log;
results.hasMore = results.log?.hasMore ?? true;
};
}

@gate()
@debug()
refresh() {
this._children = undefined;
return results as CommitsQueryResults;
};
}

private async getFilesQuery(): Promise<FilesQueryResults> {
private async getBehindFilesQuery(): Promise<FilesQueryResults> {
const diff = await Container.git.getDiffStatus(
this.uri.repoPath!,
GitRevision.createRange(
this._compareWith?.ref ?? 'HEAD',
this.compareWithWorkingTree ? '' : this.branch.ref,
this.diffComparisonNotation,
),
GitRevision.createRange(this.branch.ref, this._compareWith?.ref ?? 'HEAD', '...'),
);

return {
label: `${Strings.pluralize('file', diff !== undefined ? diff.length : 0, { zero: 'No' })} changed`,
diff: diff,
files: diff,
};
}

private async getAheadFilesQuery(): Promise<FilesQueryResults> {
let files = await Container.git.getDiffStatus(
this.uri.repoPath!,
GitRevision.createRange(this._compareWith?.ref ?? 'HEAD', this.branch.ref, '...'),
);

if (this.compareWithWorkingTree) {
const workingFiles = await Container.git.getDiffStatus(this.uri.repoPath!, 'HEAD');
if (workingFiles != null) {
if (files != null) {
for (const wf of workingFiles) {
const index = files.findIndex(f => f.fileName === wf.fileName);
if (index !== -1) {
files.splice(index, 1, wf);
} else {
files.push(wf);
}
}
} else {
files = workingFiles;
}
}
}

return {
label: `${Strings.pluralize('file', files?.length ?? 0, { zero: 'No' })} changed`,
files: files,
};
}

Expand All @@ -272,10 +282,12 @@ export class CompareBranchNode extends ViewNode<BranchesView | CommitsView | Rep
comparisons = Object.create(null) as BranchComparisons;
}

const id = `${this.branch.id}${this.branch.current ? '+current' : ''}`;

if (compareWith != null) {
comparisons[this.branch.id] = { ...compareWith };
comparisons[id] = { ...compareWith };
} else {
const { [this.branch.id]: _, ...rest } = comparisons;
const { [id]: _, ...rest } = comparisons;
comparisons = rest;
}
await Container.context.workspaceState.update(WorkspaceState.BranchComparisons, comparisons);
Expand Down

0 comments on commit 143d55b

Please sign in to comment.