From 9d45685a5a73dc93ad995292cfbdca9a3b3e1a9a Mon Sep 17 00:00:00 2001 From: Steve King Date: Tue, 31 Dec 2024 16:33:41 +0200 Subject: [PATCH 1/3] Testing `git.diffSummary` from empty tree to first commit Relates to #1035 --- .../test/integration/diff-summary.spec.ts | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 simple-git/test/integration/diff-summary.spec.ts 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..bdfff8e4 --- /dev/null +++ b/simple-git/test/integration/diff-summary.spec.ts @@ -0,0 +1,30 @@ +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; + + 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, + }) + ); + }); + }); +}); From 7ba9d0d22e337a97715e28b1ab61ba1d21737e43 Mon Sep 17 00:00:00 2001 From: Steve King Date: Tue, 31 Dec 2024 16:59:47 +0200 Subject: [PATCH 2/3] Create the `git.emptyTreeCommit()` method to get the commit hash of the repo in its initial state prior to the first commit being applied. More future-proof than relying on a hard-coded value for the hash should `git` start using the SHA256 value in place of SHA1. --- .changeset/blank-club-mauve.md | 4 ++++ simple-git/readme.md | 3 ++- simple-git/src/lib/simple-git-api.ts | 2 ++ simple-git/src/lib/tasks/empty-tree-commit.ts | 15 +++++++++++++++ .../test/integration/empty-tree-commit.spec.ts | 13 +++++++++++++ simple-git/typings/simple-git.d.ts | 5 +++++ 6 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 .changeset/blank-club-mauve.md create mode 100644 simple-git/src/lib/tasks/empty-tree-commit.ts create mode 100644 simple-git/test/integration/empty-tree-commit.spec.ts 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/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. From 0ee7121142b918b1faed9906498216865f42c56d Mon Sep 17 00:00:00 2001 From: Steve King Date: Tue, 31 Dec 2024 17:08:08 +0200 Subject: [PATCH 3/3] Logging --- simple-git/test/integration/diff-summary.spec.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/simple-git/test/integration/diff-summary.spec.ts b/simple-git/test/integration/diff-summary.spec.ts index bdfff8e4..a6554b10 100644 --- a/simple-git/test/integration/diff-summary.spec.ts +++ b/simple-git/test/integration/diff-summary.spec.ts @@ -9,6 +9,11 @@ describe('diffSummary', () => { 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);