From 6ed02ab06cea6abd9377b0d7f19be1a0c0c58742 Mon Sep 17 00:00:00 2001 From: Ash Wilson Date: Thu, 9 Aug 2018 16:03:57 -0400 Subject: [PATCH 1/2] Use deleteRef() to "reset" the initial commit --- lib/git-shell-out-strategy.js | 4 ++++ test/git-strategies.test.js | 25 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/lib/git-shell-out-strategy.js b/lib/git-shell-out-strategy.js index 011e07f83d..c8d5ae1a2b 100644 --- a/lib/git-shell-out-strategy.js +++ b/lib/git-shell-out-strategy.js @@ -812,6 +812,10 @@ export default class GitShellOutStrategy { return this.exec(['reset', `--${type}`, revision]); } + deleteRef(ref) { + return this.exec(['update-ref', '-d', ref]); + } + /** * Branches */ diff --git a/test/git-strategies.test.js b/test/git-strategies.test.js index b5c6c53c71..686138469d 100644 --- a/test/git-strategies.test.js +++ b/test/git-strategies.test.js @@ -714,6 +714,31 @@ import * as reporterProxy from '../lib/reporter-proxy'; }); }); + describe('deleteRef()', function() { + it('soft-resets an initial commit', async function() { + const workingDirPath = await cloneRepository('three-files'); + const git = createTestStrategy(workingDirPath); + + // Ensure that three-files still has only a single commit + assert.lengthOf(await git.getCommits({max: 10}), 1); + + // Put something into the index to ensure it doesn't get lost + fs.appendFileSync(path.join(workingDirPath, 'a.txt'), 'zzz\n', 'utf8'); + await git.exec(['add', '.']); + + await git.deleteRef('HEAD'); + + const after = await git.getCommit('HEAD'); + assert.isTrue(after.unbornRef); + + const stagedChanges = await git.getDiffsForFilePath('a.txt', {staged: true}); + assert.lengthOf(stagedChanges, 1); + const stagedChange = stagedChanges[0]; + assert.strictEqual(stagedChange.newPath, 'a.txt'); + assert.deepEqual(stagedChange.hunks[0].lines, ['+foo', '+zzz']); + }); + }); + describe('getBranches()', function() { const sha = '66d11860af6d28eb38349ef83de475597cb0e8b4'; From abd40e10fe8577f619ee3391d2f739dad12392b9 Mon Sep 17 00:00:00 2001 From: Ash Wilson Date: Thu, 9 Aug 2018 16:21:30 -0400 Subject: [PATCH 2/2] Fall back to update-ref -d on the initial commit --- lib/models/repository-states/present.js | 13 +++++- test/models/repository.test.js | 57 +++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/lib/models/repository-states/present.js b/lib/models/repository-states/present.js index c7b42da7c6..a6034a898b 100644 --- a/lib/models/repository-states/present.js +++ b/lib/models/repository-states/present.js @@ -341,7 +341,18 @@ export default class Present extends State { ...Keys.filePatch.eachWithOpts({staged: true}), Keys.headDescription, ], - () => this.git().reset('soft', 'HEAD~'), + async () => { + try { + await this.git().reset('soft', 'HEAD~'); + } catch (e) { + if (/unknown revision/.test(e.stdErr)) { + // Initial commit + await this.git().deleteRef('HEAD'); + } else { + throw e; + } + } + }, ); } diff --git a/test/models/repository.test.js b/test/models/repository.test.js index 0e7c9274d4..3003496211 100644 --- a/test/models/repository.test.js +++ b/test/models/repository.test.js @@ -575,6 +575,63 @@ describe('Repository', function() { }); }); + describe('undoLastCommit()', function() { + it('performs a soft reset', async function() { + const workingDirPath = await cloneRepository('multiple-commits'); + const repo = new Repository(workingDirPath); + await repo.getLoadPromise(); + + fs.appendFileSync(path.join(workingDirPath, 'file.txt'), 'qqq\n', 'utf8'); + await repo.git.exec(['add', '.']); + await repo.git.commit('add stuff'); + + const parentCommit = await repo.git.getCommit('HEAD~'); + + await repo.undoLastCommit(); + + const commitAfterReset = await repo.git.getCommit('HEAD'); + assert.strictEqual(commitAfterReset.sha, parentCommit.sha); + + const fp = await repo.getFilePatchForPath('file.txt', {staged: true}); + assert.strictEqual( + fp.toString(), + dedent` + diff --git a/file.txt b/file.txt + --- a/file.txt + +++ b/file.txt + @@ -1,1 +1,2 @@ + three + +qqq\n + `, + ); + }); + + it('deletes the HEAD ref when only a single commit is present', async function() { + const workingDirPath = await cloneRepository('three-files'); + const repo = new Repository(workingDirPath); + await repo.getLoadPromise(); + + fs.appendFileSync(path.join(workingDirPath, 'b.txt'), 'qqq\n', 'utf8'); + await repo.git.exec(['add', '.']); + + await repo.undoLastCommit(); + + const fp = await repo.getFilePatchForPath('b.txt', {staged: true}); + assert.strictEqual( + fp.toString(), + dedent` + diff --git a/b.txt b/b.txt + new file mode 100644 + --- /dev/null + +++ b/b.txt + @@ -0,0 +1,2 @@ + +bar + +qqq\n + `, + ); + }); + }); + describe('fetch(branchName, {remoteName})', function() { it('brings commits from the remote and updates remote branch, and does not update branch', async function() { const {localRepoPath} = await setUpLocalAndRemoteRepositories({remoteAhead: true});