diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index ff316ad86c8..3ebf2045b12 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -1176,7 +1176,12 @@ export class CommandCenter { } const commit = await repository.getCommit('HEAD'); - await repository.reset('HEAD~'); + if (commit.previousHashes.length > 0) { + await repository.reset('HEAD~'); + } else { + await repository.deleteRef('HEAD'); + await this.unstageAll(repository); + } repository.inputBox.value = commit.message; } diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 7f65a849ecc..14742452f66 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -501,6 +501,7 @@ export class Git { export interface Commit { hash: string; message: string; + previousHashes: string[]; } export class GitStatusParser { @@ -631,6 +632,16 @@ export function parseGitmodules(raw: string): Submodule[] { return result; } +export function parseGitCommit(raw: string): Commit | null { + const match = /^([0-9a-f]{40})\n(.*)\n([^]*)$/m.exec(raw.trim()); + if (!match) { + return null; + } + + const previousHashes = match[2] ? match[2].split(' ') : []; + return { hash: match[1], message: match[3], previousHashes }; +} + export interface DiffOptions { cached?: boolean; } @@ -928,6 +939,11 @@ export class Repository { await this.run(args); } + async deleteRef(ref: string): Promise { + const args = ['update-ref', '-d', ref]; + await this.run(args); + } + async merge(ref: string): Promise { const args = ['merge', ref]; @@ -1342,14 +1358,8 @@ export class Repository { } async getCommit(ref: string): Promise { - const result = await this.run(['show', '-s', '--format=%H\n%B', ref]); - const match = /^([0-9a-f]{40})\n([^]*)$/m.exec(result.stdout.trim()); - - if (!match) { - return Promise.reject('bad commit format'); - } - - return { hash: match[1], message: match[2] }; + const result = await this.run(['show', '-s', '--format=%H\n%P\n%B', ref]); + return parseGitCommit(result.stdout) || Promise.reject('bad commit format'); } async updateSubmodules(paths: string[]): Promise { diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 4908ddac81f..e1edda82e80 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -293,6 +293,7 @@ export enum Operation { GetCommitTemplate = 'GetCommitTemplate', DeleteBranch = 'DeleteBranch', RenameBranch = 'RenameBranch', + DeleteRef = 'DeleteRef', Merge = 'Merge', Ignore = 'Ignore', Tag = 'Tag', @@ -776,6 +777,10 @@ export class Repository implements Disposable { await this.run(Operation.Reset, () => this.repository.reset(treeish, hard)); } + async deleteRef(ref: string): Promise { + await this.run(Operation.DeleteRef, () => this.repository.deleteRef(ref)); + } + @throttle async fetch(): Promise { await this.run(Operation.Fetch, () => this.repository.fetch()); diff --git a/extensions/git/src/test/git.test.ts b/extensions/git/src/test/git.test.ts index 09661eebc9c..a1c5e196cfa 100644 --- a/extensions/git/src/test/git.test.ts +++ b/extensions/git/src/test/git.test.ts @@ -6,7 +6,7 @@ 'use strict'; import 'mocha'; -import { GitStatusParser, parseGitmodules } from '../git'; +import { GitStatusParser, parseGitCommit, parseGitmodules } from '../git'; import * as assert from 'assert'; suite('git', () => { @@ -175,4 +175,42 @@ suite('git', () => { ]); }); }); + + suite('parseGitCommit', () => { + test('single previous commit', () => { + const GIT_OUTPUT_SINGLE_PARENT = `52c293a05038d865604c2284aa8698bd087915a1 +8e5a374372b8393906c7e380dbb09349c5385554 +This is a commit message.`; + + assert.deepEqual(parseGitCommit(GIT_OUTPUT_SINGLE_PARENT), { + hash: '52c293a05038d865604c2284aa8698bd087915a1', + message: 'This is a commit message.', + previousHashes: ['8e5a374372b8393906c7e380dbb09349c5385554'] + }); + }); + + test('multiple previous commits', () => { + const GIT_OUTPUT_MULTIPLE_PARENTS = `52c293a05038d865604c2284aa8698bd087915a1 +8e5a374372b8393906c7e380dbb09349c5385554 df27d8c75b129ab9b178b386077da2822101b217 +This is a commit message.`; + + assert.deepEqual(parseGitCommit(GIT_OUTPUT_MULTIPLE_PARENTS), { + hash: '52c293a05038d865604c2284aa8698bd087915a1', + message: 'This is a commit message.', + previousHashes: ['8e5a374372b8393906c7e380dbb09349c5385554', 'df27d8c75b129ab9b178b386077da2822101b217'] + }); + }); + + test('no previous commits', async () => { + const GIT_OUTPUT_NO_PARENTS = `52c293a05038d865604c2284aa8698bd087915a1 + +This is a commit message.`; + + assert.deepEqual(parseGitCommit(GIT_OUTPUT_NO_PARENTS), { + hash: '52c293a05038d865604c2284aa8698bd087915a1', + message: 'This is a commit message.', + previousHashes: [] + }); + }); + }); }); \ No newline at end of file