diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 14138c13bc5..da619d4feb5 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -15,7 +15,7 @@ import { Git, Stash } from './git'; import { Model } from './model'; import { Repository, Resource, ResourceGroupType } from './repository'; import { applyLineChanges, getModifiedRange, intersectDiffWithRange, invertLineChange, toLineRanges } from './staging'; -import { fromGitUri, toGitUri, isGitUri } from './uri'; +import { fromGitUri, toGitUri, isGitUri, toMergeUris } from './uri'; import { grep, isDescendant, pathEquals, relativePath } from './util'; import { LogLevel, OutputChannelLogger } from './log'; import { GitTimelineItem } from './timelineProvider'; @@ -405,6 +405,51 @@ export class CommandCenter { } } + @command('_git.openMergeEditor') + async openMergeEditor(uri: unknown) { + if (!(uri instanceof Uri)) { + return; + } + const repo = this.model.getRepository(uri); + if (!repo) { + return; + } + + + type InputData = { uri: Uri; detail?: string; description?: string }; + const mergeUris = toMergeUris(uri); + let input1: InputData = { uri: mergeUris.ours }; + let input2: InputData = { uri: mergeUris.theirs }; + + try { + const [head, mergeHead] = await Promise.all([repo.getCommit('HEAD'), repo.getCommit('MERGE_HEAD')]); + // ours (current branch and commit) + input1.detail = head.refNames.map(s => s.replace(/^HEAD ->/, '')).join(', '); + input1.description = head.hash.substring(0, 7); + + // theirs + input2.detail = mergeHead.refNames.join(', '); + input2.description = mergeHead.hash.substring(0, 7); + + } catch (error) { + // not so bad, can continue with just uris + console.error('FAILED to read HEAD, MERGE_HEAD commits'); + console.error(error); + } + + const options = { + ancestor: mergeUris.base, + input1, + input2, + output: uri + }; + + await commands.executeCommand( + '_open.mergeEditor', + options + ); + } + async cloneRepository(url?: string, parentPath?: string, options: { recursive?: boolean } = {}): Promise { if (!url || typeof url !== 'string') { url = await pickRemoteSource({ diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 8fdab7f659b..a511db761a6 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -354,7 +354,7 @@ function sanitizePath(path: string): string { return path.replace(/^([a-z]):\\/i, (_, letter) => `${letter.toUpperCase()}:\\`); } -const COMMIT_FORMAT = '%H%n%aN%n%aE%n%at%n%ct%n%P%n%B'; +const COMMIT_FORMAT = '%H%n%aN%n%aE%n%at%n%ct%n%P%n%D%n%B'; export interface ICloneOptions { readonly parentPath: string; @@ -660,6 +660,7 @@ export interface Commit { authorName?: string; authorEmail?: string; commitDate?: Date; + refNames: string[]; } export class GitStatusParser { @@ -790,7 +791,7 @@ export function parseGitmodules(raw: string): Submodule[] { return result; } -const commitRegex = /([0-9a-f]{40})\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)(?:\n([^]*?))?(?:\x00)/gm; +const commitRegex = /([0-9a-f]{40})\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)(?:\n([^]*?))?(?:\x00)/gm; export function parseGitCommits(data: string): Commit[] { let commits: Commit[] = []; @@ -801,6 +802,7 @@ export function parseGitCommits(data: string): Commit[] { let authorDate; let commitDate; let parents; + let refNames; let message; let match; @@ -810,7 +812,7 @@ export function parseGitCommits(data: string): Commit[] { break; } - [, ref, authorName, authorEmail, authorDate, commitDate, parents, message] = match; + [, ref, authorName, authorEmail, authorDate, commitDate, parents, refNames, message] = match; if (message[message.length - 1] === '\n') { message = message.substr(0, message.length - 1); @@ -825,6 +827,7 @@ export function parseGitCommits(data: string): Commit[] { authorName: ` ${authorName}`.substr(1), authorEmail: ` ${authorEmail}`.substr(1), commitDate: new Date(Number(commitDate) * 1000), + refNames: refNames.split(',').map(s => s.trim()) }); } while (true); diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 4a81c8fac64..ebe1105ab94 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -13,7 +13,7 @@ import { AutoFetcher } from './autofetch'; import { debounce, memoize, throttle } from './decorators'; import { Commit, GitError, Repository as BaseRepository, Stash, Submodule, LogFileOptions } from './git'; import { StatusBarCommands } from './statusbar'; -import { toGitUri, toMergeUris } from './uri'; +import { toGitUri } from './uri'; import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, IDisposable, isDescendant, onceEvent, pathEquals, relativePath } from './util'; import { IFileWatcher, watch } from './watch'; import { LogLevel, OutputChannelLogger } from './log'; @@ -603,16 +603,10 @@ class ResourceCommandResolver { const title = this.getTitle(resource); if (!resource.leftUri && resource.rightUri && resource.type === Status.BOTH_MODIFIED) { - const mergeUris = toMergeUris(resource.rightUri); return { - command: '_open.mergeEditor', + command: '_git.openMergeEditor', title: localize('open.merge', "Open Merge"), - arguments: [{ - ancestor: mergeUris.base, - input1: mergeUris.ours, - input2: mergeUris.theirs, - output: resource.rightUri - }] + arguments: [resource.rightUri] }; } else if (!resource.leftUri) { return { diff --git a/extensions/git/src/test/git.test.ts b/extensions/git/src/test/git.test.ts index c0d0d2003c9..3b225157c3b 100644 --- a/extensions/git/src/test/git.test.ts +++ b/extensions/git/src/test/git.test.ts @@ -205,6 +205,7 @@ john.doe@mail.com 1580811030 1580811031 8e5a374372b8393906c7e380dbb09349c5385554 +main,branch This is a commit message.\x00`; assert.deepStrictEqual(parseGitCommits(GIT_OUTPUT_SINGLE_PARENT), [{ @@ -215,6 +216,7 @@ This is a commit message.\x00`; authorName: 'John Doe', authorEmail: 'john.doe@mail.com', commitDate: new Date(1580811031000), + refNames: ['main', 'branch'], }]); }); @@ -225,6 +227,7 @@ john.doe@mail.com 1580811030 1580811031 8e5a374372b8393906c7e380dbb09349c5385554 df27d8c75b129ab9b178b386077da2822101b217 +main This is a commit message.\x00`; assert.deepStrictEqual(parseGitCommits(GIT_OUTPUT_MULTIPLE_PARENTS), [{ @@ -235,6 +238,7 @@ This is a commit message.\x00`; authorName: 'John Doe', authorEmail: 'john.doe@mail.com', commitDate: new Date(1580811031000), + refNames: ['main'], }]); }); @@ -245,6 +249,7 @@ john.doe@mail.com 1580811030 1580811031 +main This is a commit message.\x00`; assert.deepStrictEqual(parseGitCommits(GIT_OUTPUT_NO_PARENTS), [{ @@ -255,6 +260,7 @@ This is a commit message.\x00`; authorName: 'John Doe', authorEmail: 'john.doe@mail.com', commitDate: new Date(1580811031000), + refNames: ['main'], }]); }); });