diff --git a/.changeset/blank-club-mauve.md b/.changeset/blank-club-mauve.md new file mode 100644 index 00000000..d7b14b0b --- /dev/null +++ b/.changeset/blank-club-mauve.md @@ -0,0 +1,4 @@ +--- +'simple-git': minor +--- +Create new interface `git.emptyTreeCommit` to obtain the commit hash for the repo prior to the first commit. diff --git a/simple-git/readme.md b/simple-git/readme.md index 09fb3be4..0d693c94 100644 --- a/simple-git/readme.md +++ b/simple-git/readme.md @@ -285,10 +285,11 @@ in v2 (deprecation notices were logged to `stdout` as `console.warn` in v2). - `.grep(searchTerm)` searches for a single search term across all files in the working tree, optionally passing a standard [options](#how-to-specify-options) object of additional arguments - `.grep(grepQueryBuilder(...))` use the `grepQueryBuilder` to create a complex query to search for, optionally passing a standard [options](#how-to-specify-options) object of additional arguments -## git hash-object +## git hash-object / hash properties - `.hashObject(filePath, write = false)` computes the object ID value for the contents of the named file (which can be outside of the work tree), optionally writing the resulting value to the object database. +- `.emptyTreeCommit()` gets the commit hash for the repo in its initial empty state before the `git.firstCommit` was applied, useful for computing the insert-only initial commit with `git.diffSummary`. ## git init diff --git a/simple-git/src/lib/simple-git-api.ts b/simple-git/src/lib/simple-git-api.ts index f24fc957..3a0a5b6c 100644 --- a/simple-git/src/lib/simple-git-api.ts +++ b/simple-git/src/lib/simple-git-api.ts @@ -5,6 +5,7 @@ import checkout from './tasks/checkout'; import countObjects from './tasks/count-objects'; import commit from './tasks/commit'; import config from './tasks/config'; +import emptyTreeCommit from './tasks/empty-tree-commit'; import firstCommit from './tasks/first-commit'; import grep from './tasks/grep'; import { hashObjectTask } from './tasks/hash-object'; @@ -147,6 +148,7 @@ Object.assign( commit(), config(), countObjects(), + emptyTreeCommit(), firstCommit(), grep(), log(), diff --git a/simple-git/src/lib/tasks/empty-tree-commit.ts b/simple-git/src/lib/tasks/empty-tree-commit.ts new file mode 100644 index 00000000..fb3f5372 --- /dev/null +++ b/simple-git/src/lib/tasks/empty-tree-commit.ts @@ -0,0 +1,15 @@ +import { Response, SimpleGit } from '../../../typings'; +import { SimpleGitApi } from '../simple-git-api'; +import { trailingFunctionArgument } from '../utils'; +import { straightThroughStringTask } from './task'; + +export default function (): Pick { + return { + emptyTreeCommit(this: SimpleGitApi): Response { + return this._runTask( + straightThroughStringTask(['hash-object', '-t', 'tree', '/dev/null'], true), + trailingFunctionArgument(arguments) + ); + }, + }; +} diff --git a/simple-git/test/integration/diff-summary.spec.ts b/simple-git/test/integration/diff-summary.spec.ts new file mode 100644 index 00000000..a6554b10 --- /dev/null +++ b/simple-git/test/integration/diff-summary.spec.ts @@ -0,0 +1,35 @@ +import { like, newSimpleGit } from '@simple-git/test-utils'; + +describe('diffSummary', () => { + it('empty tree to first commit', async () => { + const git = newSimpleGit(); + const emptyCommit = '4b825dc642cb6eb9a060e54bf8d69288fbee4904'; + const firstCommit = await git.firstCommit(); + + const task = git.diffSummary([emptyCommit, firstCommit]); + const result = await task; + + console.log( + (await git.raw('diff', '--stat=4096', emptyCommit, firstCommit)).replace(/\s/g, '#') + ); + console.log(JSON.stringify(result, null, 2)); + + expect(result.changed).toBeGreaterThan(0); + expect(result.changed).toBe(result.files.length); + expect(result.insertions).toBeGreaterThan(0); + expect(result.deletions).toBe(0); + result.files.forEach((file) => { + if (file.binary) { + throw new Error(`Test assumes no binary files in first commit`); + } + + expect(file.insertions).toBe(file.changes); + expect(file).toEqual( + like({ + changes: file.insertions, + deletions: 0, + }) + ); + }); + }); +}); diff --git a/simple-git/test/integration/empty-tree-commit.spec.ts b/simple-git/test/integration/empty-tree-commit.spec.ts new file mode 100644 index 00000000..40ad5533 --- /dev/null +++ b/simple-git/test/integration/empty-tree-commit.spec.ts @@ -0,0 +1,13 @@ +import { createTestContext } from '@simple-git/test-utils'; + +const EMPTY_SHA = '4b825dc642cb6eb9a060e54bf8d69288fbee4904'; +const EMPTY_SHA_256 = '6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321'; + +describe('empty-tree-commit', () => { + it('gets the empty tree commit', async () => { + const context = await createTestContext(); + const commit = await context.git.emptyTreeCommit(); + + expect(commit === EMPTY_SHA || commit === EMPTY_SHA_256).toBe(true); + }); +}); diff --git a/simple-git/typings/simple-git.d.ts b/simple-git/typings/simple-git.d.ts index eb75d39b..c90cc775 100644 --- a/simple-git/typings/simple-git.d.ts +++ b/simple-git/typings/simple-git.d.ts @@ -524,6 +524,11 @@ export interface SimpleGit extends SimpleGitBase { diffSummary(callback?: types.SimpleGitTaskCallback): Response; + /** + * Gets the commit hash of the initial empty repo commit, immediately prior to the first commit in the repo + */ + emptyTreeCommit(callback?: types.SimpleGitTaskCallback): Response; + /** * Sets an environment variable for the spawned child process, either supply both a name and value as strings or * a single object to entirely replace the current environment variables.