Skip to content

Commit

Permalink
Add support for git count-objects as git.countObjects
Browse files Browse the repository at this point in the history
  • Loading branch information
steveukx committed Jun 10, 2024
1 parent 4aceb15 commit 0a5378d
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/heavy-squids-look.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'simple-git': minor
---

Add support for parsing `count-objects`
4 changes: 4 additions & 0 deletions simple-git/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,10 @@ in v2 (deprecation notices were logged to `stdout` as `console.warn` in v2).
- `.listConfig()` reads the current configuration and returns a [ConfigListSummary](https://github.com/steveukx/git-js/blob/main/simple-git/src/lib/responses/ConfigList.ts)
- `.listConfig(scope: GitConfigScope)` as with `listConfig` but returns only those items in a specified scope (note that configuration values are overlaid on top of each other to build the config `git` will actually use - to resolve the configuration you are using use `(await listConfig()).all` without the scope argument)

## git count-objects

- `.countObjects()` queries the pack and disk usage properties of the local repository and returns a [CountObjectsResult](https://github.com/steveukx/git-js/blob/main/simple-git/src/lib/tasks/count-objects.ts). All disk sizes are reported in Kb, see https://git-scm.com/docs/git-count-objects for full description of properties.

## git diff

- `.diff([ options ])` get the diff of the current repo compared to the last commit, optionally including
Expand Down
2 changes: 2 additions & 0 deletions simple-git/src/lib/simple-git-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { SimpleGitBase } from '../../typings';
import { taskCallback } from './task-callback';
import { changeWorkingDirectoryTask } from './tasks/change-working-directory';
import checkout from './tasks/checkout';
import countObjects from './tasks/count-objects';
import commit from './tasks/commit';
import config from './tasks/config';
import firstCommit from './tasks/first-commit';
Expand Down Expand Up @@ -145,6 +146,7 @@ Object.assign(
checkout(),
commit(),
config(),
countObjects(),
firstCommit(),
grep(),
log(),
Expand Down
51 changes: 51 additions & 0 deletions simple-git/src/lib/tasks/count-objects.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { SimpleGitApi } from '../simple-git-api';
import type { SimpleGit } from '../../../typings';
import { asCamelCase, asNumber, LineParser, parseStringResponse } from '../utils';

export interface CountObjectsResult {
count: number;
size: number;
inPack: number;
packs: number;
sizePack: number;
prunePackable: number;
garbage: number;
sizeGarbage: number;
}

function countObjectsResponse(): CountObjectsResult {
return {
count: 0,
garbage: 0,
inPack: 0,
packs: 0,
prunePackable: 0,
size: 0,
sizeGarbage: 0,
sizePack: 0,
};
}

const parser: LineParser<CountObjectsResult> = new LineParser(
/([a-z-]+): (\d+)$/,
(result, [key, value]) => {
const property = asCamelCase(key);
if (result.hasOwnProperty(property)) {
result[property as keyof typeof result] = asNumber(value);
}
}
);

export default function (): Pick<SimpleGit, 'countObjects'> {
return {
countObjects(this: SimpleGitApi) {
return this._runTask({
commands: ['count-objects', '--verbose'],
format: 'utf-8',
parser(stdOut: string) {
return parseStringResponse(countObjectsResponse(), [parser], stdOut);
},
});
},
};
}
6 changes: 6 additions & 0 deletions simple-git/src/lib/utils/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ export function asArray<T>(source: T | T[]): T[] {
return Array.isArray(source) ? source : [source];
}

export function asCamelCase(str: string) {
return str.replace(/[\s-]+(.)/g, (_all, chr) => {
return chr.toUpperCase();
});
}

export function asStringArray<T>(source: T | T[]): string[] {
return asArray(source).map(String);
}
Expand Down
54 changes: 54 additions & 0 deletions simple-git/test/unit/count-objects.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { closeWithSuccess, like, newSimpleGit } from './__fixtures__';
import { CountObjectsResult } from '../../typings';

const COUNT_OBJ_RESPONSE = `
count: 323
size: 7920
in-pack: 8134
packs: 1
size-pack: 3916
prune-packable: 0
garbage: 0
size-garbage: 0
`;

describe('count-objects', () => {
it('gets the repo object counts', async () => {
const task = newSimpleGit().countObjects();
await closeWithSuccess(COUNT_OBJ_RESPONSE);
const objects = await task;

expect(objects).toEqual(
like({
count: 323,
size: 7920,
inPack: 8134,
packs: 1,
sizePack: 3916,
})
);
});

it('ignores unknown properties', async () => {
const task = newSimpleGit().countObjects();
await closeWithSuccess('foo: 123');
expect(await task).not.toHaveProperty('foo');
});

it('ignores invalid values', async () => {
const task = newSimpleGit().countObjects();
await closeWithSuccess('packs: error');
expect(await task).toHaveProperty('packs', 0);
});

it.each<[string, keyof CountObjectsResult, number]>([
['prune-packable', 'prunePackable', 100],
['garbage', 'garbage', 101],
['size-garbage', 'sizeGarbage', 102],
])('parses %s property', async (key, asKey, value) => {
const task = newSimpleGit().countObjects();
await closeWithSuccess(`${key}: ${value}`);

expect(await task).toEqual(like({ [asKey]: value }));
});
});
11 changes: 11 additions & 0 deletions simple-git/test/unit/utils.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
append,
asCamelCase,
asNumber,
filterArray,
filterFunction,
Expand All @@ -16,6 +17,16 @@ import {
} from '../../src/lib/utils';

describe('utils', () => {
describe('asCamelCase', () => {
it.each([
['foo-bar', 'fooBar'],
['foo-bar-baz', 'fooBarBaz'],
['foo bar baz', 'fooBarBaz'],
])('Converts %s to camelCase', (input, expected) => {
expect(asCamelCase(input)).toBe(expected);
});
});

describe('orVoid', () => {
it.each([[null], [true], [''], ['non empty string'], [[]], [{}], [0], [1]])(
'passes through %s',
Expand Down
7 changes: 7 additions & 0 deletions simple-git/typings/simple-git.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,13 @@ export interface SimpleGit extends SimpleGitBase {
callback?: types.SimpleGitTaskCallback<resp.CommitResult>
): Response<resp.CommitResult>;

/**
* Retrieves `git` disk usage information, see https://git-scm.com/docs/git-count-objects
*/
countObjects(
callback?: types.SimpleGitTaskCallback<types.VersionResult>
): Response<types.CountObjectsResult>;

/**
* Sets the path to a custom git binary, should either be `git` when there is an installation of git available on
* the system path, or a fully qualified path to the executable.
Expand Down
1 change: 1 addition & 0 deletions simple-git/typings/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export { CheckRepoActions } from '../src/lib/tasks/check-is-repo';
export { CleanOptions, CleanMode } from '../src/lib/tasks/clean';
export type { CloneOptions } from '../src/lib/tasks/clone';
export { GitConfigScope } from '../src/lib/tasks/config';
export type { CountObjectsResult } from '../src/lib/tasks/count-objects';
export { DiffNameStatus } from '../src/lib/tasks/diff-name-status';
export { GitGrepQuery, grepQueryBuilder } from '../src/lib/tasks/grep';
export { ResetOptions, ResetMode } from '../src/lib/tasks/reset';
Expand Down

0 comments on commit 0a5378d

Please sign in to comment.