git-chopstick-core 0.1.13 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -1
- package/dist/git/discover.d.ts +19 -0
- package/dist/git/discover.js +58 -5
- package/dist/git/discover.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/integration.test.ts +85 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.1.15] — 2026-06-13
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **`includeBare` option for `getRepositories`**: Bare repository detection is now implemented. Uses a heuristic check (`HEAD` + `objects/` + `refs/` via `stat`) to detect bare repos without spawning git processes. Bare repos are added to results but their internal directories (`objects/`, `refs/`, etc.) are not traversed.
|
|
7
|
+
- **`getRepositoriesSummary(rootPath, options?)`**: Combines `getRepositories` + `getRepositorySummary` for an instant workspace overview. Silently omits bare repos and unborn HEADs from results.
|
|
8
|
+
- **Integration tests**: 4 new tests covering `getRepositoriesSummary` (2) and `includeBare` (2). Total: 60 tests.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
3
12
|
## [0.1.13] — 2026-06-13
|
|
4
13
|
|
|
5
14
|
### Added
|
|
@@ -192,6 +201,7 @@
|
|
|
192
201
|
|
|
193
202
|
---
|
|
194
203
|
|
|
204
|
+
[0.1.15]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.15
|
|
195
205
|
[0.1.13]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.13
|
|
196
206
|
[0.1.11]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.11
|
|
197
207
|
[0.1.10]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.10
|
|
@@ -205,4 +215,4 @@
|
|
|
205
215
|
[0.1.2]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.2
|
|
206
216
|
[0.1.1]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.1
|
|
207
217
|
[0.1.0]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.0
|
|
208
|
-
[Unreleased]: https://github.com/parkiyong/git-chopstick-core/compare/v0.1.
|
|
218
|
+
[Unreleased]: https://github.com/parkiyong/git-chopstick-core/compare/v0.1.15...HEAD
|
package/dist/git/discover.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Repository } from '../models/repository.js';
|
|
2
|
+
import type { RepositorySummary } from './rev-parse.js';
|
|
2
3
|
/**
|
|
3
4
|
* Options for {@link getRepositories}.
|
|
4
5
|
*/
|
|
@@ -35,3 +36,21 @@ export interface GetRepositoriesOptions {
|
|
|
35
36
|
* }
|
|
36
37
|
*/
|
|
37
38
|
export declare function getRepositories(rootPath: string, options?: GetRepositoriesOptions): Promise<ReadonlyArray<Repository>>;
|
|
39
|
+
/**
|
|
40
|
+
* Discover git repositories in a directory tree and return a summary
|
|
41
|
+
* for each one.
|
|
42
|
+
*
|
|
43
|
+
* Combines {@link getRepositories} and {@link getRepositorySummary} into
|
|
44
|
+
* a single call — useful for getting an instant overview of a monorepo
|
|
45
|
+
* workspace without iterating over results manually.
|
|
46
|
+
*
|
|
47
|
+
* Repositories that fail to produce a summary (bare repos, detached HEAD
|
|
48
|
+
* with no commits, etc.) are silently omitted from the result.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* const summaries = await getRepositoriesSummary('/path/to/monorepo')
|
|
52
|
+
* for (const s of summaries) {
|
|
53
|
+
* console.log(`${s.path}: ${s.currentBranch ?? '(detached)'} @ ${s.head.slice(0, 7)}`)
|
|
54
|
+
* }
|
|
55
|
+
*/
|
|
56
|
+
export declare function getRepositoriesSummary(rootPath: string, options?: GetRepositoriesOptions): Promise<ReadonlyArray<RepositorySummary>>;
|
package/dist/git/discover.js
CHANGED
|
@@ -2,6 +2,7 @@ import { opendir, stat } from 'fs/promises';
|
|
|
2
2
|
import { join, resolve } from 'path';
|
|
3
3
|
import { Repository } from '../models/repository.js';
|
|
4
4
|
import { directoryExists } from '../lib/directory-exists.js';
|
|
5
|
+
import { getRepositorySummary } from './rev-parse.js';
|
|
5
6
|
const defaultSkipDirs = new Set([
|
|
6
7
|
'node_modules',
|
|
7
8
|
'.git',
|
|
@@ -35,10 +36,6 @@ const defaultSkipDirs = new Set([
|
|
|
35
36
|
*/
|
|
36
37
|
export async function getRepositories(rootPath, options = {}) {
|
|
37
38
|
const { depth = 5, skipDirs = defaultSkipDirs, includeBare = false, } = options;
|
|
38
|
-
if (includeBare) {
|
|
39
|
-
throw new Error('Bare repository detection is not yet implemented. ' +
|
|
40
|
-
'Set includeBare to false (default) or omit it.');
|
|
41
|
-
}
|
|
42
39
|
if (!(await directoryExists(rootPath))) {
|
|
43
40
|
return [];
|
|
44
41
|
}
|
|
@@ -58,6 +55,28 @@ export async function getRepositories(rootPath, options = {}) {
|
|
|
58
55
|
return false;
|
|
59
56
|
}
|
|
60
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Check if a directory looks like a bare git repository.
|
|
60
|
+
*
|
|
61
|
+
* A bare repo has the git internals (HEAD, objects, refs) directly in
|
|
62
|
+
* the directory rather than inside a `.git` subdirectory.
|
|
63
|
+
*
|
|
64
|
+
* This is a heuristic that checks for the essential bare-repo indicators
|
|
65
|
+
* without spawning a git process (which would be too slow for large trees).
|
|
66
|
+
*/
|
|
67
|
+
async function isBareRepo(dirPath) {
|
|
68
|
+
try {
|
|
69
|
+
const [headStat, objectsStat, refsStat] = await Promise.all([
|
|
70
|
+
stat(join(dirPath, 'HEAD')),
|
|
71
|
+
stat(join(dirPath, 'objects')),
|
|
72
|
+
stat(join(dirPath, 'refs')),
|
|
73
|
+
]);
|
|
74
|
+
return headStat.isFile() && objectsStat.isDirectory() && refsStat.isDirectory();
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
61
80
|
/**
|
|
62
81
|
* Recursively walk a directory tree looking for repos.
|
|
63
82
|
*/
|
|
@@ -73,13 +92,19 @@ export async function getRepositories(rootPath, options = {}) {
|
|
|
73
92
|
return;
|
|
74
93
|
}
|
|
75
94
|
seen.add(resolved);
|
|
76
|
-
// Check if this directory is itself a repo
|
|
95
|
+
// Check if this directory is itself a regular repo (has .git directory/file)
|
|
77
96
|
if (await hasGitDir(resolved)) {
|
|
78
97
|
repos.push(new Repository(resolved, repos.length));
|
|
79
98
|
// Continue recursing — the parent repo may contain nested repos
|
|
80
99
|
// (e.g. monorepo workspaces where the root is a repo AND packages
|
|
81
100
|
// have their own .git directories)
|
|
82
101
|
}
|
|
102
|
+
else if (includeBare && (await isBareRepo(resolved))) {
|
|
103
|
+
repos.push(new Repository(resolved, repos.length));
|
|
104
|
+
// Don't recurse into bare repos — the subdirectories are git internals
|
|
105
|
+
// (objects/, refs/, etc.) and not the working tree
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
83
108
|
// Recurse into subdirectories
|
|
84
109
|
let dir;
|
|
85
110
|
try {
|
|
@@ -121,4 +146,32 @@ export async function getRepositories(rootPath, options = {}) {
|
|
|
121
146
|
await walk(resolvedRoot, 0);
|
|
122
147
|
return repos;
|
|
123
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Discover git repositories in a directory tree and return a summary
|
|
151
|
+
* for each one.
|
|
152
|
+
*
|
|
153
|
+
* Combines {@link getRepositories} and {@link getRepositorySummary} into
|
|
154
|
+
* a single call — useful for getting an instant overview of a monorepo
|
|
155
|
+
* workspace without iterating over results manually.
|
|
156
|
+
*
|
|
157
|
+
* Repositories that fail to produce a summary (bare repos, detached HEAD
|
|
158
|
+
* with no commits, etc.) are silently omitted from the result.
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* const summaries = await getRepositoriesSummary('/path/to/monorepo')
|
|
162
|
+
* for (const s of summaries) {
|
|
163
|
+
* console.log(`${s.path}: ${s.currentBranch ?? '(detached)'} @ ${s.head.slice(0, 7)}`)
|
|
164
|
+
* }
|
|
165
|
+
*/
|
|
166
|
+
export async function getRepositoriesSummary(rootPath, options = {}) {
|
|
167
|
+
const repos = await getRepositories(rootPath, options);
|
|
168
|
+
const results = await Promise.allSettled(repos.map(r => getRepositorySummary(r.path)));
|
|
169
|
+
const summaries = [];
|
|
170
|
+
for (const result of results) {
|
|
171
|
+
if (result.status === 'fulfilled' && result.value !== null) {
|
|
172
|
+
summaries.push(result.value);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return summaries;
|
|
176
|
+
}
|
|
124
177
|
//# sourceMappingURL=discover.js.map
|
package/dist/git/discover.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discover.js","sourceRoot":"","sources":["../../src/git/discover.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAEpC,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"discover.js","sourceRoot":"","sources":["../../src/git/discover.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@ import { execSync } from 'child_process'
|
|
|
4
4
|
import { tmpdir } from 'os'
|
|
5
5
|
import { join } from 'path'
|
|
6
6
|
import {
|
|
7
|
-
Repository, getRepositories, getStatus, getWorkingDirectoryChanges, getWorkingDirectoryChangesDetailed, fileChangeSummaryToWorkingDirectoryFile, DiffSelectionType, getCommits, getBranches,
|
|
7
|
+
Repository, getRepositories, getRepositoriesSummary, getStatus, getWorkingDirectoryChanges, getWorkingDirectoryChangesDetailed, fileChangeSummaryToWorkingDirectoryFile, DiffSelectionType, getCommits, getBranches,
|
|
8
8
|
createCommit, createBranch, deleteLocalBranch, renameBranch,
|
|
9
9
|
getCurrentBranch, getRepositoryType, getRepositorySummary,
|
|
10
10
|
getRemoteUrl, getRemotesFromPath, getAllTags,
|
|
@@ -610,6 +610,90 @@ describe('getRepositories', () => {
|
|
|
610
610
|
expect(repos).toEqual([])
|
|
611
611
|
execSync(`rm -rf ${nonRepoPath}`)
|
|
612
612
|
})
|
|
613
|
+
|
|
614
|
+
it('detects bare repos when includeBare is true', async () => {
|
|
615
|
+
const root = mkdtempSync(join(tmpdir(), 'gcctest-bare-'))
|
|
616
|
+
execSync(`git init ${root}`, { stdio: 'pipe' })
|
|
617
|
+
execSync(`git -C ${root} commit --allow-empty -m 'init'`, { stdio: 'pipe' })
|
|
618
|
+
|
|
619
|
+
// Create a bare repo inside the tree
|
|
620
|
+
const bare = join(root, 'shared.git')
|
|
621
|
+
execSync(`mkdir -p ${bare}`)
|
|
622
|
+
execSync(`git init --bare ${bare}`, { stdio: 'pipe' })
|
|
623
|
+
|
|
624
|
+
// Without includeBare, should not find the bare repo
|
|
625
|
+
const reposWithout = await getRepositories(root)
|
|
626
|
+
expect(reposWithout.length).toBe(1)
|
|
627
|
+
expect(reposWithout[0].path).toBe(root)
|
|
628
|
+
|
|
629
|
+
// With includeBare, should find both
|
|
630
|
+
const reposWith = await getRepositories(root, { includeBare: true })
|
|
631
|
+
expect(reposWith.length).toBe(2)
|
|
632
|
+
|
|
633
|
+
const bareFound = reposWith.find(r => r.path === bare)
|
|
634
|
+
expect(bareFound).toBeTruthy()
|
|
635
|
+
|
|
636
|
+
execSync(`rm -rf ${root}`)
|
|
637
|
+
})
|
|
638
|
+
|
|
639
|
+
it('does not recurse into bare repo internals', async () => {
|
|
640
|
+
const root = mkdtempSync(join(tmpdir(), 'gcctest-bare-internals-'))
|
|
641
|
+
execSync(`git init ${root}`, { stdio: 'pipe' })
|
|
642
|
+
execSync(`git -C ${root} commit --allow-empty -m 'init'`, { stdio: 'pipe' })
|
|
643
|
+
|
|
644
|
+
const bare = join(root, 'project.git')
|
|
645
|
+
execSync(`mkdir -p ${bare}`)
|
|
646
|
+
execSync(`git init --bare ${bare}`, { stdio: 'pipe' })
|
|
647
|
+
|
|
648
|
+
const repos = await getRepositories(root, { includeBare: true })
|
|
649
|
+
// Should only be 2: the root repo and the bare repo. Subdirs of the bare
|
|
650
|
+
// repo like objects/, refs/ should NOT be discovered as separate repos.
|
|
651
|
+
expect(repos.length).toBe(2)
|
|
652
|
+
|
|
653
|
+
execSync(`rm -rf ${root}`)
|
|
654
|
+
})
|
|
655
|
+
})
|
|
656
|
+
|
|
657
|
+
describe('getRepositoriesSummary', () => {
|
|
658
|
+
it('returns summaries for all repos in a monorepo', async () => {
|
|
659
|
+
const root = mkdtempSync(join(tmpdir(), 'gcctest-summaries-'))
|
|
660
|
+
execSync(`git init ${root}`, { stdio: 'pipe' })
|
|
661
|
+
execSync(`git -C ${root} commit --allow-empty -m 'init'`, { stdio: 'pipe' })
|
|
662
|
+
|
|
663
|
+
const pkg = join(root, 'packages', 'pkg-a')
|
|
664
|
+
execSync(`mkdir -p ${pkg}`)
|
|
665
|
+
execSync(`git init ${pkg}`, { stdio: 'pipe' })
|
|
666
|
+
execSync(`git -C ${pkg} commit --allow-empty -m 'init pkg-a'`, { stdio: 'pipe' })
|
|
667
|
+
|
|
668
|
+
const summaries = await getRepositoriesSummary(root)
|
|
669
|
+
expect(summaries.length).toBe(2)
|
|
670
|
+
|
|
671
|
+
for (const s of summaries) {
|
|
672
|
+
expect(s.path).toBeTruthy()
|
|
673
|
+
expect(s.head).toMatch(/^[a-f0-9]{40}$/)
|
|
674
|
+
expect(s.currentBranch).toBe('main')
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
execSync(`rm -rf ${root}`)
|
|
678
|
+
})
|
|
679
|
+
|
|
680
|
+
it('skips repos that fail to produce a summary (bare repos)', async () => {
|
|
681
|
+
const root = mkdtempSync(join(tmpdir(), 'gcctest-bare-summary-'))
|
|
682
|
+
execSync(`git init ${root}`, { stdio: 'pipe' })
|
|
683
|
+
execSync(`git -C ${root} commit --allow-empty -m 'init'`, { stdio: 'pipe' })
|
|
684
|
+
|
|
685
|
+
// Create a bare repo inside the tree
|
|
686
|
+
const bare = join(root, 'bare-repo')
|
|
687
|
+
execSync(`mkdir -p ${bare}`)
|
|
688
|
+
execSync(`git init --bare ${bare}`, { stdio: 'pipe' })
|
|
689
|
+
|
|
690
|
+
const summaries = await getRepositoriesSummary(root)
|
|
691
|
+
// The bare repo should be silently skipped
|
|
692
|
+
expect(summaries.length).toBe(1)
|
|
693
|
+
expect(summaries[0].path).toBe(root)
|
|
694
|
+
|
|
695
|
+
execSync(`rm -rf ${root}`)
|
|
696
|
+
})
|
|
613
697
|
})
|
|
614
698
|
|
|
615
699
|
describe('addRemote', () => {
|