diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 957f447e8c2..0c11a690c41 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -1506,6 +1506,21 @@ export class Repository { return parseGitChanges(this.repositoryRoot, gitResult.stdout); } + async diffTrees(treeish1: string, treeish2?: string): Promise { + const args = ['diff-tree', '-r', '--name-status', '-z', '--diff-filter=ADMR', treeish1]; + + if (treeish2) { + args.push(treeish2); + } + + const gitResult = await this.exec(args); + if (gitResult.exitCode) { + return []; + } + + return parseGitChanges(this.repositoryRoot, gitResult.stdout); + } + async getMergeBase(ref1: string, ref2: string, ...refs: string[]): Promise { try { const args = ['merge-base']; diff --git a/extensions/git/src/historyProvider.ts b/extensions/git/src/historyProvider.ts index d4b1d0a39d5..1d9641474e9 100644 --- a/extensions/git/src/historyProvider.ts +++ b/extensions/git/src/historyProvider.ts @@ -146,13 +146,9 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec try { // Get the common ancestor commit, and commits - const [mergeBaseCommit, commits] = await Promise.all([ - this.repository.getCommit(options.limit.id), - this.repository.log({ range: `${options.limit.id}..`, refNames, shortStats: true }) - ]); - - // Add common ancestor commit - commits.push(mergeBaseCommit); + const commit = await this.repository.getCommit(options.limit.id); + const commitParentId = commit.parents.length > 0 ? commit.parents[0] : await this.repository.getEmptyTree(); + const commits = await this.repository.log({ range: `${commitParentId}..`, refNames, shortStats: true }); await ensureEmojis(); @@ -180,24 +176,18 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec } async provideHistoryItemSummary(historyItemId: string, historyItemParentId: string | undefined): Promise { - if (!historyItemParentId) { - const commit = await this.repository.getCommit(historyItemId); - historyItemParentId = commit.parents.length > 0 ? commit.parents[0] : `${historyItemId}^`; - } - + historyItemParentId = historyItemParentId ?? await this.repository.getEmptyTree(); const allChanges = await this.repository.diffBetweenShortStat(historyItemParentId, historyItemId); + return { id: historyItemId, parentIds: [historyItemParentId], message: '', statistics: allChanges }; } async provideHistoryItemChanges(historyItemId: string, historyItemParentId: string | undefined): Promise { - if (!historyItemParentId) { - const commit = await this.repository.getCommit(historyItemId); - historyItemParentId = commit.parents.length > 0 ? commit.parents[0] : `${historyItemId}^`; - } + historyItemParentId = historyItemParentId ?? await this.repository.getEmptyTree(); const historyItemChangesUri: Uri[] = []; const historyItemChanges: SourceControlHistoryItemChange[] = []; - const changes = await this.repository.diffBetween(historyItemParentId, historyItemId); + const changes = await this.repository.diffTrees(historyItemParentId, historyItemId); for (const change of changes) { const historyItemUri = change.uri.with({ diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 5c14345e61a..efbe5077dd4 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -718,6 +718,8 @@ export class Repository implements Disposable { private _untrackedGroup: SourceControlResourceGroup; get untrackedGroup(): GitResourceGroup { return this._untrackedGroup as GitResourceGroup; } + private _EMPTY_TREE: string | undefined; + private _HEAD: Branch | undefined; get HEAD(): Branch | undefined { return this._HEAD; @@ -1112,6 +1114,10 @@ export class Repository implements Disposable { return this.run(Operation.Diff, () => this.repository.diffBetweenShortStat(ref1, ref2)); } + diffTrees(treeish1: string, treeish2?: string): Promise { + return this.run(Operation.Diff, () => this.repository.diffTrees(treeish1, treeish2)); + } + getMergeBase(ref1: string, ref2: string, ...refs: string[]): Promise { return this.run(Operation.MergeBase, () => this.repository.getMergeBase(ref1, ref2, ...refs)); } @@ -1602,6 +1608,15 @@ export class Repository implements Disposable { return await this.repository.getCommit(ref); } + async getEmptyTree(): Promise { + if (!this._EMPTY_TREE) { + const result = await this.repository.exec(['hash-object', '-t', 'tree', '/dev/null']); + this._EMPTY_TREE = result.stdout.trim(); + } + + return this._EMPTY_TREE; + } + async getCommitCount(range: string): Promise<{ ahead: number; behind: number }> { return await this.run(Operation.RevList, () => this.repository.getCommitCount(range)); } diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index b3e022bdb0b..33a8b8f5671 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -1948,12 +1948,11 @@ registerAction2(class extends Action2 { const title = historyItems.length === 1 ? `${historyItems[0].id.substring(0, 8)} - ${historyItems[0].message}` : - localize('historyItemChangesEditorTitle', "All Changes ({0} ↔ {1})", historyItem.id.substring(0, 8), historyItemLast.id.substring(0, 8)); + localize('historyItemChangesEditorTitle', "All Changes ({0} ↔ {1})", historyItemLast.id.substring(0, 8), historyItem.id.substring(0, 8)); const rootUri = provider.rootUri; - const multiDiffSourceUri = rootUri ? - rootUri.with({ scheme: 'scm-history-item', path: `${rootUri.path}/${historyItem.id}..${historyItemParentId}` }) : - URI.from({ scheme: 'scm-history-item', path: `${provider.label}/${historyItem.id}..${historyItemParentId}` }, true); + const path = rootUri ? rootUri.path : provider.label; + const multiDiffSourceUri = URI.from({ scheme: 'scm-history-item', path: `${path}/${historyItemParentId}..${historyItem.id}` }, true); commandService.executeCommand('_workbench.openMultiDiffEditor', { title, multiDiffSourceUri, resources: historyItemChanges }); } @@ -3420,9 +3419,8 @@ export class SCMViewPane extends ViewPane { const title = `${historyItem.id.substring(0, 8)} - ${historyItem.message}`; const rootUri = e.element.repository.provider.rootUri; - const multiDiffSourceUri = rootUri ? - rootUri.with({ scheme: 'scm-history-item', path: `${rootUri.path}/${historyItem.id}..${historyItemParentId}` }) : - URI.from({ scheme: 'scm-history-item', path: `${e.element.repository.provider.label}/${historyItem.id}..${historyItemParentId}` }, true); + const path = rootUri ? rootUri.path : e.element.repository.provider.label; + const multiDiffSourceUri = URI.from({ scheme: 'scm-history-item', path: `${path}/${historyItemParentId}..${historyItem.id}` }, true); await this.commandService.executeCommand('_workbench.openMultiDiffEditor', { title, multiDiffSourceUri, resources: historyItemChanges }); }