Skip to content

Commit d37d191

Browse files
committed
Enhance createGitReader() to automatically attempt adding the .git folder, add isGitDir() and resolveGitDir()
1 parent ce4cede commit d37d191

File tree

4 files changed

+123
-5
lines changed

4 files changed

+123
-5
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
- Added `repo.currentBranch()` method
44
- Added `repo.describeRef(ref)` method, which returns an information object about the reference
55
- Added `repo.isOid(value)` method to check if a value is an object ID
6+
- Added `isGitDir()` and `resolveGitDir()` helper functions
7+
- Enhanced `createGitReader()` to automatically attempt adding the `.git` folder to the provided `gitdir` path, making explicit inclusion of `.git` optional
68

79
## 0.1.3 (2023-10-13)
810

src/gitdir.ts

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { stat } from 'fs/promises';
2+
import { join, resolve } from 'path';
3+
4+
const test = {
5+
objects: 'dir',
6+
refs: 'dir',
7+
HEAD: 'file',
8+
config: 'file'
9+
};
10+
11+
export async function isGitDir(dir: string) {
12+
try {
13+
const stats = await Promise.all(Object.keys(test).map((name) => stat(join(dir, name))));
14+
15+
return Object.values(test).every((type, idx) =>
16+
type === 'dir' ? stats[idx].isDirectory() : stats[idx].isFile()
17+
);
18+
} catch {
19+
return false;
20+
}
21+
}
22+
23+
export async function resolveGitDir(dir: string) {
24+
const absdir = resolve(process.cwd(), dir);
25+
let absdirStat;
26+
27+
try {
28+
absdirStat = await stat(absdir);
29+
} catch {
30+
throw new Error(`No such directory "${absdir}"`);
31+
}
32+
33+
if (!absdirStat.isDirectory()) {
34+
throw new Error(`Not a directory "${absdir}"`);
35+
}
36+
37+
try {
38+
const dotGitDir = join(absdir, '.git');
39+
await stat(dotGitDir);
40+
return dotGitDir;
41+
} catch {}
42+
43+
return absdir;
44+
}

src/index.ts

+13-5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { isGitDir, resolveGitDir } from './gitdir.js';
12
import { createReadObject } from './read-object.js';
23
import { createRefIndex } from './resolve-ref.js';
34
import { createLooseObjectIndex } from './loose-object-index.js';
@@ -9,21 +10,23 @@ import { GitReaderOptions, NormalizedGitReaderOptions, CruftPackMode } from './t
910

1011
export * from './types.js';
1112
export * from './parse-object.js';
13+
export { isGitDir, resolveGitDir };
1214

1315
export async function createGitReader(gitdir: string, options?: GitReaderOptions) {
1416
const startInitTime = Date.now();
1517
const normalizedOptions = normalizeOptions(options);
18+
const resolvedGitDir = await resolveGitDir(gitdir);
1619
const [refIndex, looseObjectIndex, packedObjectIndex] = await Promise.all([
17-
createRefIndex(gitdir),
18-
createLooseObjectIndex(gitdir),
19-
createPackedObjectIndex(gitdir, normalizedOptions)
20+
createRefIndex(resolvedGitDir),
21+
createLooseObjectIndex(resolvedGitDir),
22+
createPackedObjectIndex(resolvedGitDir, normalizedOptions)
2023
]);
2124
const { readObjectHeaderByHash, readObjectByHash, readObjectHeaderByOid, readObjectByOid } =
2225
createReadObject(looseObjectIndex, packedObjectIndex);
2326

2427
return {
2528
get gitdir() {
26-
return gitdir;
29+
return resolvedGitDir;
2730
},
2831
readObjectHeaderByHash,
2932
readObjectByHash,
@@ -35,7 +38,12 @@ export async function createGitReader(gitdir: string, options?: GitReaderOptions
3538
async dispose() {
3639
await Promise.all([looseObjectIndex.dispose(), packedObjectIndex.dispose()]);
3740
},
38-
stat: createStatMethod({ gitdir, refIndex, looseObjectIndex, packedObjectIndex }),
41+
stat: createStatMethod({
42+
gitdir: resolvedGitDir,
43+
refIndex,
44+
looseObjectIndex,
45+
packedObjectIndex
46+
}),
3947

4048
initTime: Date.now() - startInitTime
4149
};

test/gitdir.ts

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import assert from 'assert';
2+
import path from 'path';
3+
import { isGitDir, resolveGitDir } from '@discoveryjs/scan-git';
4+
5+
const cwd = process.cwd();
6+
const gitdir = path.join(cwd, '.git');
7+
8+
describe('Git Directory Utilities', () => {
9+
describe('isGitDir()', () => {
10+
it('should return false for non-Git directory', async () => {
11+
const actual = await isGitDir(cwd);
12+
assert.strictEqual(actual, false);
13+
});
14+
15+
it('should return true for valid Git directory', async () => {
16+
const actual = await isGitDir(gitdir);
17+
assert.strictEqual(actual, true);
18+
});
19+
});
20+
21+
describe('resolveGitDir()', () => {
22+
describe('absolute paths', () => {
23+
it('should resolve current working directory to Git directory', async () => {
24+
const actual = await resolveGitDir(cwd);
25+
assert.strictEqual(actual, gitdir);
26+
});
27+
28+
it('should resolve .git directory path to itself', async () => {
29+
const actual = await resolveGitDir(gitdir);
30+
assert.strictEqual(actual, gitdir);
31+
});
32+
});
33+
34+
describe('relative paths', () => {
35+
it('should resolve empty path to Git directory', async () => {
36+
const actual = await resolveGitDir('');
37+
assert.strictEqual(actual, gitdir);
38+
});
39+
40+
it('should resolve .git relative path to Git directory', async () => {
41+
const actual = await resolveGitDir('.git');
42+
assert.strictEqual(actual, gitdir);
43+
});
44+
});
45+
46+
it('should throws for non existing paths', () => {
47+
const testdir = path.resolve(cwd, 'non-exists');
48+
49+
return assert.rejects(
50+
() => resolveGitDir(testdir),
51+
(e: Error) => e.message.endsWith(`No such directory "${testdir}"`)
52+
);
53+
});
54+
55+
it('should throws for non directory paths', () => {
56+
const testpath = path.resolve(cwd, 'package.json');
57+
58+
return assert.rejects(
59+
() => resolveGitDir(testpath),
60+
(e: Error) => e.message.endsWith(`Not a directory "${testpath}"`)
61+
);
62+
});
63+
});
64+
});

0 commit comments

Comments
 (0)