git-chopstick-core 0.1.11 → 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 CHANGED
@@ -1,5 +1,31 @@
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
+
12
+ ## [0.1.13] — 2026-06-13
13
+
14
+ ### Added
15
+ - **`getRepositories(rootPath, options?)`**: New `src/git/discover.ts` module for discovering git repos in a directory tree. Walks directories using `fs.opendir` with depth control (default 5), skips `node_modules`/`.git`/`.yarn`/etc., handles symlinks and worktree `.git` files. Returns `Repository[]`.
16
+ - **`WorkingDirectoryFileChangeSummary`** type: Like `WorkingDirectoryChangeSummary` but includes `selectionType` and `selection` for staging decisions.
17
+ - **`getWorkingDirectoryChangesDetailed(repository)`**: Selection-aware variant of `getWorkingDirectoryChanges` that returns `WorkingDirectoryFileChangeSummary[]` with `DiffSelection` objects.
18
+ - **`fileChangeSummaryToWorkingDirectoryFile(summary)`**: Lossy roundtrip helper to convert a `WorkingDirectoryFileChangeSummary` back to `WorkingDirectoryFileChange` for use with `stageFiles()`.
19
+ - **Integration tests**: 8 new tests covering `getWorkingDirectoryChangesDetailed` (3), `fileChangeSummaryToWorkingDirectoryFile` roundtrip (2), and `getRepositories` (5). Total: 56 tests.
20
+
21
+ ### Changed
22
+ - **README Known Limitations**: Removed stale "Integration Tests" item from Known Limitations section (not a limitation). Cleaned up section.
23
+ - **README API reference**: Added `discover` module, `getWorkingDirectoryChangesDetailed`, `fileChangeSummaryToWorkingDirectoryFile`, `WorkingDirectoryChangeSummary`, and `WorkingDirectoryFileChangeSummary` to tables.
24
+ - **`src/git/index.ts`**: Added `discover.js` to barrel exports.
25
+ - **`src/git/status.ts`**: Re-exports `DiffSelectionType` and `DiffSelection` at module level for convenient imports.
26
+
27
+ ---
28
+
3
29
  ## [0.1.11] — 2026-06-13
4
30
 
5
31
  ### Added
@@ -175,6 +201,8 @@
175
201
 
176
202
  ---
177
203
 
204
+ [0.1.15]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.15
205
+ [0.1.13]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.13
178
206
  [0.1.11]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.11
179
207
  [0.1.10]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.10
180
208
  [0.1.9]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.9
@@ -187,4 +215,4 @@
187
215
  [0.1.2]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.2
188
216
  [0.1.1]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.1
189
217
  [0.1.0]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.0
190
- [Unreleased]: https://github.com/parkiyong/git-chopstick-core/compare/v0.1.11...HEAD
218
+ [Unreleased]: https://github.com/parkiyong/git-chopstick-core/compare/v0.1.15...HEAD
package/README.md CHANGED
@@ -213,6 +213,7 @@ try {
213
213
  | `config` | `getConfigValue`, `getGlobalConfigValue`, `getBooleanConfigValue` | Read git config |
214
214
  | `diff` | `getWorkingDirectoryDiff`, `getCommitDiff`, `getBranchMergeBaseDiff`, `getCommitRangeDiff`, `getBinaryPaths` | Diff rendering |
215
215
  | `diff-index` | `getIndexChanges` | Compare index with tree |
216
+ | `discover` | `getRepositories` | Discover git repos in a directory tree (monorepo support) |
216
217
  | `fetch` | `fetch`, `fetchRefspec`, `fastForwardBranches` | Fetch from remotes |
217
218
  | `for-each-ref` | `getBranches`, `getBranchesDifferingFromUpstream` | List refs |
218
219
  | `format-patch` | `formatPatch` | Generate patch files |
@@ -238,7 +239,7 @@ try {
238
239
  | `squash` | `squash` | Interactive rebase squashing |
239
240
  | `stage` | `stageManualConflictResolution`, `stageResolvedConflictFiles` | Stage conflict resolutions |
240
241
  | `stash` | `getStashes`, `getStashesByPath`, `createStashEntry`, `popStashEntry`, `getStashedFiles`, `dropStashEntry` | Stash management |
241
- | `status` | `getStatus` | Repository status |
242
+ | `status` | `getStatus`, `getWorkingDirectoryChanges`, `getWorkingDirectoryChangesDetailed`, `fileChangeSummaryToWorkingDirectoryFile` | Repository status + flat summaries + selection-aware variant with roundtrip |
242
243
  | `submodule` | `updateSubmodulesAfterOperation`, `listSubmodules`, `resetSubmodulePaths` | Submodule operations |
243
244
  | `tag` | `createTag`, `deleteTag`, `getTags`, `getAllTags` | Tag management |
244
245
  | `update-index` | `stageFiles` | Stage files |
@@ -263,6 +264,8 @@ try {
263
264
  | `WorkingDirectoryFileChange` | class | Uncommitted file change with diff selection |
264
265
  | `CommittedFileChange` | class | File change from a commit |
265
266
  | `WorkingDirectoryStatus` | class | Container for working directory changes |
267
+ | `WorkingDirectoryChangeSummary` | type | `{ path: string; status: string; oldPath?: string }` — flattened working directory change |
268
+ | `WorkingDirectoryFileChangeSummary` | type | Like `WorkingDirectoryChangeSummary` but also includes `selectionType` + `selection` for staging decisions |
266
269
  | `DiffSelection` / `DiffSelectionType` | class/enum | Partial file staging |
267
270
  | `ComputedAction` | enum | `Clean`, `Conflicts`, `Invalid`, `Loading` |
268
271
  | `ManualConflictResolution` | enum | `theirs` / `ours` |
@@ -295,10 +298,6 @@ Note: `git revert` does not produce progress output, so `RevertProgressParser` s
295
298
 
296
299
  `withTrampolineEnv` is a **stub** that calls through without setting up the Git LFS trampoline environment. Git LFS operations may not work correctly as a result.
297
300
 
298
- ### 🟢 Integration Tests
299
-
300
- Integration tests are available in `src/__tests__/integration.test.ts` and run against real git repositories in temp directories. Run `npm test` to execute them.
301
-
302
301
  ---
303
302
 
304
303
  ## Architecture
@@ -0,0 +1,56 @@
1
+ import { Repository } from '../models/repository.js';
2
+ import type { RepositorySummary } from './rev-parse.js';
3
+ /**
4
+ * Options for {@link getRepositories}.
5
+ */
6
+ export interface GetRepositoriesOptions {
7
+ /**
8
+ * Maximum recursion depth. Defaults to 5.
9
+ * Set to `-1` for unlimited depth.
10
+ */
11
+ readonly depth?: number;
12
+ /**
13
+ * Directories to skip when walking the tree.
14
+ * Defaults to `['node_modules', '.git', '.hg', '.svn', '.yarn', '.pnp']`.
15
+ */
16
+ readonly skipDirs?: ReadonlySet<string>;
17
+ /**
18
+ * Whether to include bare repositories. Defaults to `false`.
19
+ * Bare repos have no working directory and `Repository.path` will
20
+ * point to the bare git directory.
21
+ */
22
+ readonly includeBare?: boolean;
23
+ }
24
+ /**
25
+ * Discover git repositories in a directory tree.
26
+ *
27
+ * Walks the directory tree under `rootPath`, looking for `.git` entries
28
+ * (directories for regular repos, or files for worktrees and submodules).
29
+ * Returns the discovered repositories as an array of `Repository` objects.
30
+ *
31
+ * @example
32
+ * // Find all repos up to 3 levels deep, excluding node_modules
33
+ * const repos = await getRepositories('/path/to/monorepo', { depth: 3 })
34
+ * for (const repo of repos) {
35
+ * console.log(repo.name, repo.path)
36
+ * }
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>>;
@@ -0,0 +1,177 @@
1
+ import { opendir, stat } from 'fs/promises';
2
+ import { join, resolve } from 'path';
3
+ import { Repository } from '../models/repository.js';
4
+ import { directoryExists } from '../lib/directory-exists.js';
5
+ import { getRepositorySummary } from './rev-parse.js';
6
+ const defaultSkipDirs = new Set([
7
+ 'node_modules',
8
+ '.git',
9
+ '.hg',
10
+ '.svn',
11
+ '.yarn',
12
+ '.pnp',
13
+ '__pycache__',
14
+ '.cache',
15
+ '.next',
16
+ '.turbo',
17
+ 'dist',
18
+ 'build',
19
+ '.venv',
20
+ 'vendor',
21
+ '.tox',
22
+ ]);
23
+ /**
24
+ * Discover git repositories in a directory tree.
25
+ *
26
+ * Walks the directory tree under `rootPath`, looking for `.git` entries
27
+ * (directories for regular repos, or files for worktrees and submodules).
28
+ * Returns the discovered repositories as an array of `Repository` objects.
29
+ *
30
+ * @example
31
+ * // Find all repos up to 3 levels deep, excluding node_modules
32
+ * const repos = await getRepositories('/path/to/monorepo', { depth: 3 })
33
+ * for (const repo of repos) {
34
+ * console.log(repo.name, repo.path)
35
+ * }
36
+ */
37
+ export async function getRepositories(rootPath, options = {}) {
38
+ const { depth = 5, skipDirs = defaultSkipDirs, includeBare = false, } = options;
39
+ if (!(await directoryExists(rootPath))) {
40
+ return [];
41
+ }
42
+ const resolvedRoot = resolve(rootPath);
43
+ const repos = [];
44
+ const seen = new Set();
45
+ /**
46
+ * Check if a path contains a `.git` entry, indicating a git repository.
47
+ */
48
+ async function hasGitDir(dirPath) {
49
+ const gitPath = join(dirPath, '.git');
50
+ try {
51
+ const s = await stat(gitPath);
52
+ return s.isDirectory() || s.isFile();
53
+ }
54
+ catch {
55
+ return false;
56
+ }
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
+ }
80
+ /**
81
+ * Recursively walk a directory tree looking for repos.
82
+ */
83
+ async function walk(currentPath, currentDepth) {
84
+ // Check depth limit
85
+ if (depth !== -1 && currentDepth > depth) {
86
+ return;
87
+ }
88
+ // Resolve to avoid duplicate entries from symlinks
89
+ const resolved = resolve(currentPath);
90
+ // Skip if already seen (e.g., symlink loop)
91
+ if (seen.has(resolved)) {
92
+ return;
93
+ }
94
+ seen.add(resolved);
95
+ // Check if this directory is itself a regular repo (has .git directory/file)
96
+ if (await hasGitDir(resolved)) {
97
+ repos.push(new Repository(resolved, repos.length));
98
+ // Continue recursing — the parent repo may contain nested repos
99
+ // (e.g. monorepo workspaces where the root is a repo AND packages
100
+ // have their own .git directories)
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
+ }
108
+ // Recurse into subdirectories
109
+ let dir;
110
+ try {
111
+ dir = await opendir(resolved);
112
+ }
113
+ catch {
114
+ return; // Permission denied, etc.
115
+ }
116
+ for await (const entry of dir) {
117
+ if (entry.name.startsWith('.')) {
118
+ // Skip hidden directories (except the root itself)
119
+ // But .gitignore, .github, etc. could have submodules
120
+ // Only skip .git, .hg, .svn
121
+ if (entry.name === '.git' || entry.name === '.hg' || entry.name === '.svn') {
122
+ continue;
123
+ }
124
+ }
125
+ if (skipDirs.has(entry.name)) {
126
+ continue;
127
+ }
128
+ if (entry.isDirectory()) {
129
+ await walk(join(resolved, entry.name), currentDepth + 1);
130
+ }
131
+ else if (entry.isSymbolicLink()) {
132
+ // Follow symlinks to directories
133
+ const linkPath = join(resolved, entry.name);
134
+ try {
135
+ const linkStat = await stat(linkPath);
136
+ if (linkStat.isDirectory()) {
137
+ await walk(linkPath, currentDepth + 1);
138
+ }
139
+ }
140
+ catch {
141
+ // Broken symlink, skip
142
+ }
143
+ }
144
+ }
145
+ }
146
+ await walk(resolvedRoot, 0);
147
+ return repos;
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
+ }
177
+ //# sourceMappingURL=discover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.js","sourceRoot":"","sources":["../../src/git/discover.ts"],"names":[],"mappings":"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"}
@@ -6,6 +6,7 @@ export * from './checkout.js';
6
6
  export * from './checkout-index.js';
7
7
  export * from './cherry-pick.js';
8
8
  export * from './clean.js';
9
+ export * from './discover.js';
9
10
  export * from './clone.js';
10
11
  export * from './commit.js';
11
12
  export * from './config.js';
package/dist/git/index.js CHANGED
@@ -8,6 +8,7 @@ export * from './checkout.js';
8
8
  export * from './checkout-index.js';
9
9
  export * from './cherry-pick.js';
10
10
  export * from './clean.js';
11
+ export * from './discover.js';
11
12
  export * from './clone.js';
12
13
  export * from './commit.js';
13
14
  export * from './config.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/git/index.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,4BAA4B,EAAE,SAAS,EAAyB,MAAM,WAAW,CAAC;AAEjH,uBAAuB;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,qBAAqB,CAAA;AACnC,cAAc,kBAAkB,CAAA;AAChC,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,kBAAkB,CAAA;AAChC,cAAc,WAAW,CAAA;AACzB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,YAAY,CAAA;AAC1B,cAAc,mBAAmB,CAAA;AACjC,cAAc,mBAAmB,CAAA;AACjC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,WAAW,CAAA;AACzB,cAAc,yBAAyB,CAAA;AACvC,cAAc,UAAU,CAAA;AACxB,cAAc,UAAU,CAAA;AACxB,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA;AAC7B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,SAAS,CAAA;AACvB,cAAc,WAAW,CAAA;AACzB,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA;AACxB,cAAc,mBAAmB,CAAA;AACjC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,UAAU,CAAA;AACxB,cAAc,eAAe,CAAA;AAC7B,cAAc,uBAAuB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/git/index.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,4BAA4B,EAAE,SAAS,EAAyB,MAAM,WAAW,CAAC;AAEjH,uBAAuB;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,qBAAqB,CAAA;AACnC,cAAc,kBAAkB,CAAA;AAChC,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,kBAAkB,CAAA;AAChC,cAAc,WAAW,CAAA;AACzB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,YAAY,CAAA;AAC1B,cAAc,mBAAmB,CAAA;AACjC,cAAc,mBAAmB,CAAA;AACjC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,WAAW,CAAA;AACzB,cAAc,yBAAyB,CAAA;AACvC,cAAc,UAAU,CAAA;AACxB,cAAc,UAAU,CAAA;AACxB,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA;AAC7B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,SAAS,CAAA;AACvB,cAAc,WAAW,CAAA;AACzB,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA;AACxB,cAAc,mBAAmB,CAAA;AACjC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,UAAU,CAAA;AACxB,cAAc,eAAe,CAAA;AAC7B,cAAc,uBAAuB,CAAA"}
@@ -1,7 +1,9 @@
1
- import { WorkingDirectoryStatus } from '../models/status.js';
1
+ import { WorkingDirectoryStatus, WorkingDirectoryFileChange } from '../models/status.js';
2
+ import { DiffSelectionType, DiffSelection } from '../models/diff/index.js';
2
3
  import { Repository } from '../models/repository.js';
3
4
  import { IAheadBehind } from '../models/branch.js';
4
5
  import { RebaseInternalState } from '../models/rebase.js';
6
+ export { DiffSelectionType, DiffSelection } from '../models/diff/index.js';
5
7
  /** The encapsulation of the result from 'git status' */
6
8
  export interface IStatusResult {
7
9
  /** The name of the current branch */
@@ -37,3 +39,83 @@ export declare function getStatus(repository: Repository): Promise<IStatusResult
37
39
  export declare function getStatus(repository: Repository, includeUntracked: boolean): Promise<IStatusResult | null>;
38
40
  export declare function getStatus(repository: Repository, includeUntracked: boolean, rejectOnError: true): Promise<IStatusResult>;
39
41
  export declare function getStatus(repository: Repository, includeUntracked: boolean, rejectOnError: false): Promise<IStatusResult | null>;
42
+ /**
43
+ * A flattened summary of a working directory change.
44
+ *
45
+ * Saves consumers from digging into the nested `WorkingDirectoryFileChange.status`
46
+ * union type to get the path, status string, and optional oldPath.
47
+ */
48
+ export interface WorkingDirectoryChangeSummary {
49
+ readonly path: string;
50
+ readonly status: 'added' | 'modified' | 'deleted' | 'renamed' | 'copied' | 'conflicted' | 'untracked';
51
+ readonly oldPath?: string;
52
+ }
53
+ /**
54
+ * Get a flattened list of working directory changes for a repository.
55
+ *
56
+ * Wraps `getStatus` and maps each `WorkingDirectoryFileChange` to a simple
57
+ * `WorkingDirectoryChangeSummary` object, eliminating the need for consumers
58
+ * to dig into the `AppFileStatus` union type.
59
+ *
60
+ * Throws if the repository path is not a valid git repository.
61
+ */
62
+ export declare function getWorkingDirectoryChanges(repository: Repository): Promise<ReadonlyArray<WorkingDirectoryChangeSummary>>;
63
+ /**
64
+ * A flattened summary of a working directory change that also includes
65
+ * the `DiffSelection` for each file, enabling consumers to programmatically
66
+ * stage/unstage individual files or partial hunks.
67
+ *
68
+ * Use `fileChangeSummaryToWorkingDirectoryFile()` to convert back to
69
+ * `WorkingDirectoryFileChange[]` for use with `stageFiles()`.
70
+ */
71
+ export interface WorkingDirectoryFileChangeSummary {
72
+ readonly path: string;
73
+ readonly status: 'added' | 'modified' | 'deleted' | 'renamed' | 'copied' | 'conflicted' | 'untracked';
74
+ readonly oldPath?: string;
75
+ /**
76
+ * Whether this file is selected for commit.
77
+ * - `All`: fully staged for commit
78
+ * - `None`: excluded from commit
79
+ * - `Partial`: only selected hunks/lines are staged
80
+ */
81
+ readonly selectionType: DiffSelectionType;
82
+ /**
83
+ * The full `DiffSelection` object. Use the `withLineSelection()` or
84
+ * `withSelectAll()` / `withSelectNone()` methods to modify the selection,
85
+ * then pass through `fileChangeSummaryToWorkingDirectoryFile()` to
86
+ * create `WorkingDirectoryFileChange` instances for `stageFiles()`.
87
+ */
88
+ readonly selection: DiffSelection;
89
+ }
90
+ /**
91
+ * Get a detailed list of working directory changes, including the
92
+ * `DiffSelection` for each file.
93
+ *
94
+ * Unlike `getWorkingDirectoryChanges()`, this variant includes the selection
95
+ * state so consumers can programmatically stage/unstage individual files
96
+ * or partial hunks.
97
+ *
98
+ * Round-trip with `fileChangeSummaryToWorkingDirectoryFile()` to convert
99
+ * back to `WorkingDirectoryFileChange[]` for use with `stageFiles()`.
100
+ *
101
+ * @see getWorkingDirectoryChanges for a lighter variant without selection info
102
+ * @see fileChangeSummaryToWorkingDirectoryFile to convert back to WorkingDirectoryFileChange
103
+ */
104
+ export declare function getWorkingDirectoryChangesDetailed(repository: Repository): Promise<ReadonlyArray<WorkingDirectoryFileChangeSummary>>;
105
+ /**
106
+ * Convert a `WorkingDirectoryFileChangeSummary` back to a
107
+ * `WorkingDirectoryFileChange` for use with `stageFiles()`.
108
+ *
109
+ * This is a lossy conversion — the reconstructed `AppFileStatus` contains
110
+ * only the information available in the summary (path, status kind, oldPath).
111
+ * Submodule status and conflict marker counts are not preserved.
112
+ *
113
+ * @example
114
+ * const changes = await getWorkingDirectoryChangesDetailed(repo)
115
+ * // Exclude deleted files from the commit
116
+ * const toStage = changes
117
+ * .filter(c => c.status !== 'deleted')
118
+ * .map(fileChangeSummaryToWorkingDirectoryFile)
119
+ * await stageFiles(repo, toStage)
120
+ */
121
+ export declare function fileChangeSummaryToWorkingDirectoryFile(summary: WorkingDirectoryFileChangeSummary): WorkingDirectoryFileChange;
@@ -8,6 +8,9 @@ import { getBinaryPaths } from './diff.js';
8
8
  import { getRebaseInternalState } from './rebase.js';
9
9
  import { isCherryPickHeadFound } from './cherry-pick.js';
10
10
  import { git } from './index.js';
11
+ // Re-export DiffSelectionType and DiffSelection so consumers can import from the barrel
12
+ // without importing directly from models/diff
13
+ export { DiffSelectionType, DiffSelection } from '../models/diff/index.js';
11
14
  function parseConflictedState(entry, path, conflictDetails) {
12
15
  switch (entry.action) {
13
16
  case UnmergedEntrySummary.BothAdded: {
@@ -221,6 +224,140 @@ function parseStatusHeader(results, header) {
221
224
  match,
222
225
  };
223
226
  }
227
+ /**
228
+ * Get a flattened list of working directory changes for a repository.
229
+ *
230
+ * Wraps `getStatus` and maps each `WorkingDirectoryFileChange` to a simple
231
+ * `WorkingDirectoryChangeSummary` object, eliminating the need for consumers
232
+ * to dig into the `AppFileStatus` union type.
233
+ *
234
+ * Throws if the repository path is not a valid git repository.
235
+ */
236
+ export async function getWorkingDirectoryChanges(repository) {
237
+ const status = await getStatus(repository, true, true);
238
+ return status.workingDirectory.files.map(file => {
239
+ const summary = {
240
+ path: file.path,
241
+ status: statusKindToString(file.status.kind),
242
+ };
243
+ if ((file.status.kind === AppFileStatusKind.Renamed ||
244
+ file.status.kind === AppFileStatusKind.Copied) &&
245
+ 'oldPath' in file.status &&
246
+ file.status.oldPath !== undefined) {
247
+ return { ...summary, oldPath: file.status.oldPath };
248
+ }
249
+ return summary;
250
+ });
251
+ }
252
+ function statusKindToString(kind) {
253
+ switch (kind) {
254
+ case AppFileStatusKind.New:
255
+ return 'added';
256
+ case AppFileStatusKind.Modified:
257
+ return 'modified';
258
+ case AppFileStatusKind.Deleted:
259
+ return 'deleted';
260
+ case AppFileStatusKind.Renamed:
261
+ return 'renamed';
262
+ case AppFileStatusKind.Copied:
263
+ return 'copied';
264
+ case AppFileStatusKind.Conflicted:
265
+ return 'conflicted';
266
+ case AppFileStatusKind.Untracked:
267
+ return 'untracked';
268
+ }
269
+ }
270
+ /**
271
+ * Get a detailed list of working directory changes, including the
272
+ * `DiffSelection` for each file.
273
+ *
274
+ * Unlike `getWorkingDirectoryChanges()`, this variant includes the selection
275
+ * state so consumers can programmatically stage/unstage individual files
276
+ * or partial hunks.
277
+ *
278
+ * Round-trip with `fileChangeSummaryToWorkingDirectoryFile()` to convert
279
+ * back to `WorkingDirectoryFileChange[]` for use with `stageFiles()`.
280
+ *
281
+ * @see getWorkingDirectoryChanges for a lighter variant without selection info
282
+ * @see fileChangeSummaryToWorkingDirectoryFile to convert back to WorkingDirectoryFileChange
283
+ */
284
+ export async function getWorkingDirectoryChangesDetailed(repository) {
285
+ const status = await getStatus(repository, true, true);
286
+ return status.workingDirectory.files.map(file => {
287
+ const summary = {
288
+ path: file.path,
289
+ status: statusKindToString(file.status.kind),
290
+ selectionType: file.selection.getSelectionType(),
291
+ selection: file.selection,
292
+ };
293
+ if ((file.status.kind === AppFileStatusKind.Renamed ||
294
+ file.status.kind === AppFileStatusKind.Copied) &&
295
+ 'oldPath' in file.status &&
296
+ file.status.oldPath !== undefined) {
297
+ return { ...summary, oldPath: file.status.oldPath };
298
+ }
299
+ return summary;
300
+ });
301
+ }
302
+ /**
303
+ * Convert a `WorkingDirectoryFileChangeSummary` back to a
304
+ * `WorkingDirectoryFileChange` for use with `stageFiles()`.
305
+ *
306
+ * This is a lossy conversion — the reconstructed `AppFileStatus` contains
307
+ * only the information available in the summary (path, status kind, oldPath).
308
+ * Submodule status and conflict marker counts are not preserved.
309
+ *
310
+ * @example
311
+ * const changes = await getWorkingDirectoryChangesDetailed(repo)
312
+ * // Exclude deleted files from the commit
313
+ * const toStage = changes
314
+ * .filter(c => c.status !== 'deleted')
315
+ * .map(fileChangeSummaryToWorkingDirectoryFile)
316
+ * await stageFiles(repo, toStage)
317
+ */
318
+ export function fileChangeSummaryToWorkingDirectoryFile(summary) {
319
+ let appStatus;
320
+ switch (summary.status) {
321
+ case 'added':
322
+ appStatus = { kind: AppFileStatusKind.New };
323
+ break;
324
+ case 'modified':
325
+ appStatus = { kind: AppFileStatusKind.Modified };
326
+ break;
327
+ case 'deleted':
328
+ appStatus = { kind: AppFileStatusKind.Deleted };
329
+ break;
330
+ case 'renamed':
331
+ appStatus = {
332
+ kind: AppFileStatusKind.Renamed,
333
+ oldPath: summary.oldPath ?? summary.path,
334
+ renameIncludesModifications: false,
335
+ };
336
+ break;
337
+ case 'copied':
338
+ appStatus = {
339
+ kind: AppFileStatusKind.Copied,
340
+ oldPath: summary.oldPath ?? summary.path,
341
+ renameIncludesModifications: false,
342
+ };
343
+ break;
344
+ case 'conflicted':
345
+ appStatus = {
346
+ kind: AppFileStatusKind.Conflicted,
347
+ entry: {
348
+ kind: 'conflicted',
349
+ action: UnmergedEntrySummary.BothModified,
350
+ us: GitStatusEntry.UpdatedButUnmerged,
351
+ them: GitStatusEntry.UpdatedButUnmerged,
352
+ },
353
+ };
354
+ break;
355
+ case 'untracked':
356
+ appStatus = { kind: AppFileStatusKind.Untracked };
357
+ break;
358
+ }
359
+ return new WorkingDirectoryFileChange(summary.path, appStatus, summary.selection);
360
+ }
224
361
  async function getMergeConflictDetails(repository, conflictedFilesInIndex) {
225
362
  const conflictCountsByPath = await getFilesWithConflictMarkers(repository.path);
226
363
  const binaryFilePaths = await getBinaryPaths(repository, 'MERGE_HEAD', conflictedFilesInIndex);
@@ -1 +1 @@
1
- {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/git/status.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/git/status.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-chopstick-core",
3
- "version": "0.1.11",
3
+ "version": "0.1.15",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -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, getStatus, 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,
@@ -89,6 +89,116 @@ describe('getStatus', () => {
89
89
  })
90
90
  })
91
91
 
92
+ describe('getWorkingDirectoryChanges', () => {
93
+ it('returns flattened changes for a repo with modified files', async () => {
94
+ // Modify an existing file and create a new (untracked) one
95
+ writeFileSync(join(repoPath, 'README.md'), 'modified content')
96
+ writeFileSync(join(repoPath, 'new-file.txt'), 'new content')
97
+
98
+ const changes = await getWorkingDirectoryChanges(repo)
99
+
100
+ expect(changes.length).toBeGreaterThanOrEqual(2)
101
+
102
+ const modified = changes.find(c => c.path === 'README.md')
103
+ expect(modified).toBeTruthy()
104
+ expect(modified!.status).toBe('modified')
105
+ expect(modified!.oldPath).toBeUndefined()
106
+
107
+ const untracked = changes.find(c => c.path === 'new-file.txt')
108
+ expect(untracked).toBeTruthy()
109
+ // Unstaged new files show as 'untracked' (not 'added' — that requires staging)
110
+ expect(untracked!.status).toBe('untracked')
111
+
112
+ // Clean up
113
+ git(repoPath, 'checkout -- README.md')
114
+ execSync(`rm -f ${join(repoPath, 'new-file.txt')}`)
115
+ })
116
+
117
+ it('throws for a non-repo path', async () => {
118
+ const nonRepoPath = mkdtempSync(join(tmpdir(), 'gcctest-nonrepo-'))
119
+ await expect(
120
+ getWorkingDirectoryChanges(new Repository(nonRepoPath))
121
+ ).rejects.toThrow()
122
+ execSync(`rm -rf ${nonRepoPath}`)
123
+ })
124
+
125
+ it('includes oldPath for renamed files', async () => {
126
+ // Use an existing tracked file to avoid creating a commit
127
+ git(repoPath, 'mv README.md readme-backup.md')
128
+
129
+ const changes = await getWorkingDirectoryChanges(repo)
130
+ const renamed = changes.find(c => c.path === 'readme-backup.md')
131
+ expect(renamed).toBeTruthy()
132
+ expect(renamed!.status).toBe('renamed')
133
+ expect(renamed!.oldPath).toBe('README.md')
134
+
135
+ // Clean up: reverse the rename
136
+ git(repoPath, 'mv readme-backup.md README.md')
137
+ })
138
+ })
139
+
140
+ describe('getWorkingDirectoryChangesDetailed', () => {
141
+ it('includes selectionType and selection for each file', async () => {
142
+ // Modify an existing file
143
+ writeFileSync(join(repoPath, 'README.md'), 'detailed test')
144
+
145
+ const changes = await getWorkingDirectoryChangesDetailed(repo)
146
+
147
+ expect(changes.length).toBeGreaterThanOrEqual(1)
148
+
149
+ const modified = changes.find(c => c.path === 'README.md')
150
+ expect(modified).toBeTruthy()
151
+ expect(modified!.status).toBe('modified')
152
+ expect(modified!.selectionType).toBe(DiffSelectionType.All)
153
+ expect(modified!.selection).toBeTruthy()
154
+ // DiffSelection should report the file as fully selected
155
+ expect(modified!.selection.isSelected(0)).toBe(true)
156
+
157
+ // Clean up
158
+ git(repoPath, 'checkout -- README.md')
159
+ })
160
+
161
+ it('round-trips through fileChangeSummaryToWorkingDirectoryFile', async () => {
162
+ // Create a file and stage it so it appears as 'added'
163
+ writeFileSync(join(repoPath, 'stage-test.txt'), 'stage me')
164
+ git(repoPath, 'add stage-test.txt')
165
+
166
+ const changes = await getWorkingDirectoryChangesDetailed(repo)
167
+ const added = changes.find(c => c.path === 'stage-test.txt')
168
+ expect(added).toBeTruthy()
169
+ expect(added!.status).toBe('added')
170
+
171
+ // Round-trip back to WorkingDirectoryFileChange
172
+ const wdfc = fileChangeSummaryToWorkingDirectoryFile(added!)
173
+ expect(wdfc.path).toBe('stage-test.txt')
174
+ expect(wdfc.selection.getSelectionType()).toBe(DiffSelectionType.All)
175
+ // Verify the status was properly reconstructed
176
+ expect(wdfc.status.kind).toBe(AppFileStatusKind.New)
177
+
178
+ // Clean up: unstage and remove
179
+ git(repoPath, 'reset HEAD -- stage-test.txt')
180
+ execSync(`rm -f ${join(repoPath, 'stage-test.txt')}`)
181
+ })
182
+
183
+ it('preserves oldPath through round-trip for renamed files', async () => {
184
+ git(repoPath, 'mv README.md readme-backup.md')
185
+
186
+ const changes = await getWorkingDirectoryChangesDetailed(repo)
187
+ const renamed = changes.find(c => c.path === 'readme-backup.md')
188
+ expect(renamed).toBeTruthy()
189
+ expect(renamed!.oldPath).toBe('README.md')
190
+
191
+ // Round-trip
192
+ const wdfc = fileChangeSummaryToWorkingDirectoryFile(renamed!)
193
+ expect(wdfc.path).toBe('readme-backup.md')
194
+ expect(wdfc.status.kind).toBe(AppFileStatusKind.Renamed)
195
+ expect((wdfc.status as any).oldPath).toBe('README.md')
196
+
197
+ // Clean up: reverse the rename
198
+ git(repoPath, 'mv readme-backup.md README.md')
199
+ })
200
+ })
201
+
92
202
  describe('getCommits', () => {
93
203
  it('returns commits in order (fixture has 6 commits reachable from main)', async () => {
94
204
  const commits = await getCommits(repo, 'HEAD', 10)
@@ -419,6 +529,173 @@ describe('getRemotesFromPath', () => {
419
529
  })
420
530
  })
421
531
 
532
+ describe('getRepositories', () => {
533
+ it('discovers the root repo when scanning from repo path', async () => {
534
+ // Scan the fixture repo — should find it at the root
535
+ const repos = await getRepositories(repoPath)
536
+ expect(repos.length).toBeGreaterThanOrEqual(1)
537
+
538
+ const root = repos.find(r => r.path === repoPath)
539
+ expect(root).toBeTruthy()
540
+ // The fixture is created in a temp dir, so name will be the temp dir name
541
+ expect(root!.path).toBe(repoPath)
542
+ })
543
+
544
+ it('finds nested repos in a monorepo-style layout', async () => {
545
+ const monorepoRoot = mkdtempSync(join(tmpdir(), 'gcctest-monorepo-'))
546
+ execSync(`git init ${monorepoRoot}`, { stdio: 'pipe' })
547
+ execSync(`git -C ${monorepoRoot} commit --allow-empty -m 'init'`, { stdio: 'pipe' })
548
+
549
+ const pkgA = join(monorepoRoot, 'packages', 'pkg-a')
550
+ const pkgB = join(monorepoRoot, 'packages', 'pkg-b')
551
+ execSync(`mkdir -p ${pkgA} ${pkgB}`)
552
+ execSync(`git init ${pkgA}`, { stdio: 'pipe' })
553
+ execSync(`git -C ${pkgA} commit --allow-empty -m 'init pkg-a'`, { stdio: 'pipe' })
554
+ execSync(`git init ${pkgB}`, { stdio: 'pipe' })
555
+ execSync(`git -C ${pkgB} commit --allow-empty -m 'init pkg-b'`, { stdio: 'pipe' })
556
+
557
+ const repos = await getRepositories(monorepoRoot)
558
+ expect(repos.length).toBe(3)
559
+
560
+ const paths = repos.map(r => r.path).sort()
561
+ expect(paths).toContain(monorepoRoot)
562
+ expect(paths).toContain(pkgA)
563
+ expect(paths).toContain(pkgB)
564
+
565
+ execSync(`rm -rf ${monorepoRoot}`)
566
+ })
567
+
568
+ it('skips node_modules directories', async () => {
569
+ const root = mkdtempSync(join(tmpdir(), 'gcctest-skips-'))
570
+ execSync(`git init ${root}`, { stdio: 'pipe' })
571
+ execSync(`git -C ${root} commit --allow-empty -m 'init'`, { stdio: 'pipe' })
572
+
573
+ const nm = join(root, 'node_modules', 'some-lib')
574
+ execSync(`mkdir -p ${nm}`)
575
+ execSync(`git init ${nm}`, { stdio: 'pipe' })
576
+ execSync(`git -C ${nm} commit --allow-empty -m 'dep'`, { stdio: 'pipe' })
577
+
578
+ const repos = await getRepositories(root)
579
+ expect(repos.length).toBe(1)
580
+ expect(repos[0].path).toBe(root)
581
+
582
+ execSync(`rm -rf ${root}`)
583
+ })
584
+
585
+ it('obeys the depth limit', async () => {
586
+ const root = mkdtempSync(join(tmpdir(), 'gcctest-depth-'))
587
+ execSync(`git init ${root}`, { stdio: 'pipe' })
588
+ execSync(`git -C ${root} commit --allow-empty -m 'init'`, { stdio: 'pipe' })
589
+
590
+ const deep = join(root, 'a', 'b', 'c')
591
+ execSync(`mkdir -p ${deep}`)
592
+ execSync(`git init ${deep}`, { stdio: 'pipe' })
593
+ execSync(`git -C ${deep} commit --allow-empty -m 'deep'`, { stdio: 'pipe' })
594
+
595
+ // With depth 2, should not find the deep repo
596
+ const repos = await getRepositories(root, { depth: 2 })
597
+ expect(repos.length).toBe(1)
598
+ expect(repos[0].path).toBe(root)
599
+
600
+ // With depth -1 (unlimited), should find the deep repo
601
+ const reposDeep = await getRepositories(root, { depth: -1 })
602
+ expect(reposDeep.length).toBe(2)
603
+
604
+ execSync(`rm -rf ${root}`)
605
+ })
606
+
607
+ it('returns empty array for a non-repo path', async () => {
608
+ const nonRepoPath = mkdtempSync(join(tmpdir(), 'gcctest-nonrepo-'))
609
+ const repos = await getRepositories(nonRepoPath)
610
+ expect(repos).toEqual([])
611
+ execSync(`rm -rf ${nonRepoPath}`)
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
+ })
697
+ })
698
+
422
699
  describe('addRemote', () => {
423
700
  it('adds a new remote and returns it', async () => {
424
701
  const remote = await addRemote(repo, 'upstream', 'https://github.com/upstream/repo.git')