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 #44776 - Error on "Undo Last Commit" if executed against the initial commit #47578

Merged
merged 9 commits into from
Jul 6, 2018
7 changes: 6 additions & 1 deletion extensions/git/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1123,7 +1123,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;
}

Expand Down
26 changes: 18 additions & 8 deletions extensions/git/src/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,7 @@ export class Git {
export interface Commit {
hash: string;
message: string;
previousHashes: string[];
}

export class GitStatusParser {
Expand Down Expand Up @@ -611,6 +612,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;
}
Expand Down Expand Up @@ -894,6 +905,11 @@ export class Repository {
await this.run(args);
}

async deleteRef(ref: string): Promise<void> {
const args = ['update-ref', '-d', ref];
await this.run(args);
}

async merge(ref: string): Promise<void> {
const args = ['merge', ref];

Expand Down Expand Up @@ -1287,14 +1303,8 @@ export class Repository {
}

async getCommit(ref: string): Promise<Commit> {
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<Commit>('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<Commit>('bad commit format');
}

async updateSubmodules(paths: string[]): Promise<void> {
Expand Down
5 changes: 5 additions & 0 deletions extensions/git/src/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ export enum Operation {
GetCommitTemplate = 'GetCommitTemplate',
DeleteBranch = 'DeleteBranch',
RenameBranch = 'RenameBranch',
DeleteRef = 'DeleteRef',
Merge = 'Merge',
Ignore = 'Ignore',
Tag = 'Tag',
Expand Down Expand Up @@ -722,6 +723,10 @@ export class Repository implements Disposable {
await this.run(Operation.Reset, () => this.repository.reset(treeish, hard));
}

async deleteRef(ref: string): Promise<void> {
await this.run(Operation.DeleteRef, () => this.repository.deleteRef(ref));
}

@throttle
async fetch(): Promise<void> {
await this.run(Operation.Fetch, () => this.repository.fetch());
Expand Down
40 changes: 39 additions & 1 deletion extensions/git/src/test/git.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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: []
});
});
});
});