From 7ad0c44c7362c476ab1526e02b4c2e4a09b2e5f5 Mon Sep 17 00:00:00 2001 From: Nick Lucas Date: Tue, 2 Nov 2021 13:38:52 +0000 Subject: [PATCH 1/2] Improve handling of parent-less commits for diffs, and when switching to entirely different trees --- packages/git/src/GitDiff.test.ts | 21 +++++++++++ packages/git/src/GitDiff.ts | 64 ++++++++++++++++++++++++++++++-- 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/packages/git/src/GitDiff.test.ts b/packages/git/src/GitDiff.test.ts index 5bc42a41..09ad2b0a 100644 --- a/packages/git/src/GitDiff.test.ts +++ b/packages/git/src/GitDiff.test.ts @@ -135,6 +135,27 @@ describe('Git', () => { }) }) }) + + describe('corner cases', () => { + it('gets diff for parent-less commit', async () => { + const sha = await getBaseCommit() + const git = new Git(dir) + + const changes = await git.diff.getByShas(sha) + expect(changes!).toEqual({ + files: [ + { + oldName: null, + newName: 'onlyfile', + isDeleted: false, + isModified: false, + isNew: true, + isRenamed: false, + }, + ], + }) + }) + }) }) describe('getDiffFromIndex', () => { diff --git a/packages/git/src/GitDiff.ts b/packages/git/src/GitDiff.ts index 0c9b1f67..34fdb23d 100644 --- a/packages/git/src/GitDiff.ts +++ b/packages/git/src/GitDiff.ts @@ -36,12 +36,31 @@ export class GitDiff { } } + // Just support git syntax here, but it does need converting internally + if (sha?.includes('~')) { + const [childSha] = sha.split('~') + sha = await this.getShaParent(childSha) + } + const fileType = path.extname(filePath) let plainText = null if (sha) { const cmd = ['show', `${sha}:${filePath}`] - plainText = await spawn(cmd) + try { + plainText = await spawn(cmd) + } catch (err) { + if ( + /[(' does not exist in ')|( exists on disk, but not in )]/.test( + String(err), + ) + ) { + // If the file is not in the commit, that's fine, it just didn't exist yet and so is empty + plainText = '' + } else { + throw err + } + } } else { const absoluteFilePath = path.join(this._cwd, filePath) plainText = await new Promise((resolve, reject) => { @@ -72,9 +91,12 @@ export class GitDiff { return null } - if (!shaOld) { - shaOld = `${shaNew}~1` + // While sha~1 syntax can go back a commit for free, it will error for parent-less commits. + // We look up the parent commit properly so parent-less commits can be compared to the empty tree instead + if (shaOld == null) { + shaOld = await this.getShaParent(shaNew) } + const cmd = [ 'diff', shaOld, @@ -153,4 +175,40 @@ export class GitDiff { }), } } + + private emptyTreeShaMemo: string | null = null + private getEmptyTreeSha = async (): Promise => { + const spawn = (await this._getSpawn())! + + if (this.emptyTreeShaMemo == null) { + this.emptyTreeShaMemo = await spawn([ + 'hash-object', + '-t', + 'tree', + '/dev/null', + ]) + this.emptyTreeShaMemo = this.emptyTreeShaMemo.trim() + } + + return this.emptyTreeShaMemo + } + + private getShaParent = async (sha: string): Promise => { + const spawn = (await this._getSpawn())! + + const output = await spawn(['rev-list', '--parents', '-n', '1', sha]) + + const shas = output.split(/[\s]+/) + if (shas[0] != sha) { + throw `getShaParent failed. Unexpected output: "${output}" for input "${sha}"` + } + + if (!!shas[1]) { + // Merge commits will have a 3rd element, but but we always want the mainline parent for diffs + return shas[1] + } else { + // Unparented commits will... not have a parent + return await this.getEmptyTreeSha() + } + } } From b838bf3e75d5166a16843676d96a581f93d0a6db Mon Sep 17 00:00:00 2001 From: Nick Lucas Date: Tue, 2 Nov 2021 13:39:08 +0000 Subject: [PATCH 2/2] Increment version --- packages/giterm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/giterm/package.json b/packages/giterm/package.json index 00760ec7..eed1daaf 100644 --- a/packages/giterm/package.json +++ b/packages/giterm/package.json @@ -1,6 +1,6 @@ { "name": "giterm", - "version": "0.20.0", + "version": "0.20.1", "description": "A git gui for terminal lovers", "homepage": "https://github.com/Nick-Lucas/giterm", "main": "init.js",