Skip to content

Commit

Permalink
Enhance createGitReader() to automatically attempt adding the `.git…
Browse files Browse the repository at this point in the history
…` folder, add `isGitDir()` and `resolveGitDir()`
  • Loading branch information
lahmatiy committed Oct 31, 2024
1 parent ce4cede commit d37d191
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 5 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
- Added `repo.currentBranch()` method
- Added `repo.describeRef(ref)` method, which returns an information object about the reference
- Added `repo.isOid(value)` method to check if a value is an object ID
- Added `isGitDir()` and `resolveGitDir()` helper functions
- Enhanced `createGitReader()` to automatically attempt adding the `.git` folder to the provided `gitdir` path, making explicit inclusion of `.git` optional

## 0.1.3 (2023-10-13)

Expand Down
44 changes: 44 additions & 0 deletions src/gitdir.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { stat } from 'fs/promises';
import { join, resolve } from 'path';

const test = {
objects: 'dir',
refs: 'dir',
HEAD: 'file',
config: 'file'
};

export async function isGitDir(dir: string) {
try {
const stats = await Promise.all(Object.keys(test).map((name) => stat(join(dir, name))));

return Object.values(test).every((type, idx) =>
type === 'dir' ? stats[idx].isDirectory() : stats[idx].isFile()
);
} catch {
return false;
}
}

export async function resolveGitDir(dir: string) {
const absdir = resolve(process.cwd(), dir);
let absdirStat;

try {
absdirStat = await stat(absdir);
} catch {
throw new Error(`No such directory "${absdir}"`);
}

if (!absdirStat.isDirectory()) {
throw new Error(`Not a directory "${absdir}"`);
}

try {
const dotGitDir = join(absdir, '.git');
await stat(dotGitDir);
return dotGitDir;
} catch {}

return absdir;
}
18 changes: 13 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { isGitDir, resolveGitDir } from './gitdir.js';
import { createReadObject } from './read-object.js';
import { createRefIndex } from './resolve-ref.js';
import { createLooseObjectIndex } from './loose-object-index.js';
Expand All @@ -9,21 +10,23 @@ import { GitReaderOptions, NormalizedGitReaderOptions, CruftPackMode } from './t

export * from './types.js';
export * from './parse-object.js';
export { isGitDir, resolveGitDir };

export async function createGitReader(gitdir: string, options?: GitReaderOptions) {
const startInitTime = Date.now();
const normalizedOptions = normalizeOptions(options);
const resolvedGitDir = await resolveGitDir(gitdir);
const [refIndex, looseObjectIndex, packedObjectIndex] = await Promise.all([
createRefIndex(gitdir),
createLooseObjectIndex(gitdir),
createPackedObjectIndex(gitdir, normalizedOptions)
createRefIndex(resolvedGitDir),
createLooseObjectIndex(resolvedGitDir),
createPackedObjectIndex(resolvedGitDir, normalizedOptions)
]);
const { readObjectHeaderByHash, readObjectByHash, readObjectHeaderByOid, readObjectByOid } =
createReadObject(looseObjectIndex, packedObjectIndex);

return {
get gitdir() {
return gitdir;
return resolvedGitDir;
},
readObjectHeaderByHash,
readObjectByHash,
Expand All @@ -35,7 +38,12 @@ export async function createGitReader(gitdir: string, options?: GitReaderOptions
async dispose() {
await Promise.all([looseObjectIndex.dispose(), packedObjectIndex.dispose()]);
},
stat: createStatMethod({ gitdir, refIndex, looseObjectIndex, packedObjectIndex }),
stat: createStatMethod({
gitdir: resolvedGitDir,
refIndex,
looseObjectIndex,
packedObjectIndex
}),

initTime: Date.now() - startInitTime
};
Expand Down
64 changes: 64 additions & 0 deletions test/gitdir.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import assert from 'assert';
import path from 'path';
import { isGitDir, resolveGitDir } from '@discoveryjs/scan-git';

const cwd = process.cwd();
const gitdir = path.join(cwd, '.git');

describe('Git Directory Utilities', () => {
describe('isGitDir()', () => {
it('should return false for non-Git directory', async () => {
const actual = await isGitDir(cwd);
assert.strictEqual(actual, false);
});

it('should return true for valid Git directory', async () => {
const actual = await isGitDir(gitdir);
assert.strictEqual(actual, true);
});
});

describe('resolveGitDir()', () => {
describe('absolute paths', () => {
it('should resolve current working directory to Git directory', async () => {
const actual = await resolveGitDir(cwd);
assert.strictEqual(actual, gitdir);
});

it('should resolve .git directory path to itself', async () => {
const actual = await resolveGitDir(gitdir);
assert.strictEqual(actual, gitdir);
});
});

describe('relative paths', () => {
it('should resolve empty path to Git directory', async () => {
const actual = await resolveGitDir('');
assert.strictEqual(actual, gitdir);
});

it('should resolve .git relative path to Git directory', async () => {
const actual = await resolveGitDir('.git');
assert.strictEqual(actual, gitdir);
});
});

it('should throws for non existing paths', () => {
const testdir = path.resolve(cwd, 'non-exists');

return assert.rejects(
() => resolveGitDir(testdir),
(e: Error) => e.message.endsWith(`No such directory "${testdir}"`)
);
});

it('should throws for non directory paths', () => {
const testpath = path.resolve(cwd, 'package.json');

return assert.rejects(
() => resolveGitDir(testpath),
(e: Error) => e.message.endsWith(`Not a directory "${testpath}"`)
);
});
});
});

0 comments on commit d37d191

Please sign in to comment.