@huhuhang/multipull 0.1.0
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/LICENSE +21 -0
- package/README.md +101 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +84 -0
- package/dist/cli.js.map +1 -0
- package/dist/defaults.d.ts +5 -0
- package/dist/defaults.js +32 -0
- package/dist/defaults.js.map +1 -0
- package/dist/discover.d.ts +5 -0
- package/dist/discover.js +89 -0
- package/dist/discover.js.map +1 -0
- package/dist/git.d.ts +23 -0
- package/dist/git.js +236 -0
- package/dist/git.js.map +1 -0
- package/dist/runner.d.ts +3 -0
- package/dist/runner.js +171 -0
- package/dist/runner.js.map +1 -0
- package/dist/table.d.ts +2 -0
- package/dist/table.js +36 -0
- package/dist/table.js.map +1 -0
- package/dist/types.d.ts +38 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 huhuhang
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# multipull
|
|
2
|
+
|
|
3
|
+
Pull many Git repositories safely from one CLI.
|
|
4
|
+
|
|
5
|
+
`multipull` scans a directory tree for real Git repositories, skips noisy generated folders, and runs a conservative pull operation for each repository.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Discovers repository roots before running Git commands.
|
|
10
|
+
- Skips common heavy directories such as `node_modules`, `dist`, `build`, `target`, `vendor`, `lab-*`, and `challenge-*`.
|
|
11
|
+
- Uses a safe pull strategy: `git pull --ff-only --prune`.
|
|
12
|
+
- Skips dirty worktrees by default.
|
|
13
|
+
- Skips detached HEAD and branches without an upstream.
|
|
14
|
+
- Shows one clear final table with repository, branch, upstream, and result.
|
|
15
|
+
- Optionally parks dirty work on a `multipull-backup/*` branch before switching to the default branch.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install -g @huhuhang/multipull
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Development checkout:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
git clone https://github.com/huhuhang/multipull.git
|
|
27
|
+
cd multipull
|
|
28
|
+
npm install
|
|
29
|
+
npm run build
|
|
30
|
+
npm link
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
multipull [paths...]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Examples:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
multipull
|
|
43
|
+
multipull ~/GitHub
|
|
44
|
+
multipull ~/GitHub ~/Work
|
|
45
|
+
multipull --dry-run
|
|
46
|
+
multipull --verbose
|
|
47
|
+
multipull --park-to-default-branch
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Options
|
|
51
|
+
|
|
52
|
+
```text
|
|
53
|
+
--dry-run Show planned actions without pulling.
|
|
54
|
+
--verbose Show Git output details after the summary table.
|
|
55
|
+
--park-to-default-branch Preserve dirty changes before switching to the default branch.
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The public CLI is intentionally small. Concurrency, scan depth, timeout, and ignore rules are internal defaults so the command stays easy to use.
|
|
59
|
+
|
|
60
|
+
## Default Behavior
|
|
61
|
+
|
|
62
|
+
For each discovered repository, `multipull`:
|
|
63
|
+
|
|
64
|
+
1. Reads the current branch, upstream, and worktree status.
|
|
65
|
+
2. Skips dirty worktrees unless `--park-to-default-branch` is enabled and a default branch switch is needed.
|
|
66
|
+
3. Skips detached HEAD.
|
|
67
|
+
4. Skips branches without an upstream.
|
|
68
|
+
5. Runs `git pull --ff-only --prune`.
|
|
69
|
+
6. Prints a final summary table.
|
|
70
|
+
|
|
71
|
+
## Parking Local Work
|
|
72
|
+
|
|
73
|
+
`--park-to-default-branch` is explicit because it creates Git history and switches branches.
|
|
74
|
+
|
|
75
|
+
When enabled:
|
|
76
|
+
|
|
77
|
+
- If the repository is already on its default branch, `multipull` pulls normally.
|
|
78
|
+
- If the repository is on another clean branch, `multipull` switches to the default branch and pulls.
|
|
79
|
+
- If the repository is on another dirty branch, `multipull` creates a `multipull-backup/*` branch, commits the current worktree there, switches to the default branch, and pulls.
|
|
80
|
+
|
|
81
|
+
Ignored files are not force-added or deleted.
|
|
82
|
+
|
|
83
|
+
## Development
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npm install
|
|
87
|
+
npm run typecheck
|
|
88
|
+
npm test
|
|
89
|
+
npm run build
|
|
90
|
+
npm run check
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Run from source:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
npm run dev -- ~/GitHub --dry-run
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## License
|
|
100
|
+
|
|
101
|
+
MIT
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { DEFAULT_MAX_DEPTH } from "./defaults.js";
|
|
4
|
+
import { discoverRepos } from "./discover.js";
|
|
5
|
+
import { ok, runGit } from "./git.js";
|
|
6
|
+
import { renderTable } from "./table.js";
|
|
7
|
+
import { runRepos } from "./runner.js";
|
|
8
|
+
function printDetails(results, verbose) {
|
|
9
|
+
const detailItems = results.filter((item) => {
|
|
10
|
+
if (verbose) {
|
|
11
|
+
return Boolean(item.output || item.detail);
|
|
12
|
+
}
|
|
13
|
+
return item.category === "fail" && Boolean(item.detail || item.output);
|
|
14
|
+
});
|
|
15
|
+
if (detailItems.length === 0) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
console.log();
|
|
19
|
+
console.log("Details");
|
|
20
|
+
console.log("-------");
|
|
21
|
+
for (const item of detailItems) {
|
|
22
|
+
console.log(`${item.repo.label}: ${item.detail || item.result}`);
|
|
23
|
+
if (verbose && item.output) {
|
|
24
|
+
for (const line of item.output.split(/\r?\n/)) {
|
|
25
|
+
console.log(` ${line}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export async function main(argv = process.argv) {
|
|
31
|
+
const program = new Command()
|
|
32
|
+
.name("multipull")
|
|
33
|
+
.description("Pull many Git repositories safely.")
|
|
34
|
+
.argument("[paths...]", "root directories to scan", ["."])
|
|
35
|
+
.option("--dry-run", "show planned actions without pulling")
|
|
36
|
+
.option("--verbose", "show git output details after the summary table")
|
|
37
|
+
.option("--park-to-default-branch", "preserve dirty changes on a multipull-backup/* branch before switching to the default branch")
|
|
38
|
+
.showHelpAfterError();
|
|
39
|
+
program.parse(argv);
|
|
40
|
+
const paths = program.args.length > 0 ? program.args : ["."];
|
|
41
|
+
const options = program.opts();
|
|
42
|
+
if (!ok(await runGit(process.cwd(), ["--version"], 10_000))) {
|
|
43
|
+
console.error("multipull: git command not found");
|
|
44
|
+
return 127;
|
|
45
|
+
}
|
|
46
|
+
const { repos, missing } = await discoverRepos(paths, { maxDepth: DEFAULT_MAX_DEPTH });
|
|
47
|
+
for (const path of missing) {
|
|
48
|
+
console.error(`multipull: skipped missing directory: ${path}`);
|
|
49
|
+
}
|
|
50
|
+
if (repos.length === 0) {
|
|
51
|
+
console.log("No Git repositories found.");
|
|
52
|
+
return missing.length > 0 ? 1 : 0;
|
|
53
|
+
}
|
|
54
|
+
const results = await runRepos(repos, {
|
|
55
|
+
dryRun: options.dryRun ?? false,
|
|
56
|
+
verbose: options.verbose ?? false,
|
|
57
|
+
parkToDefaultBranch: options.parkToDefaultBranch ?? false
|
|
58
|
+
});
|
|
59
|
+
console.log(renderTable(results));
|
|
60
|
+
const counts = {
|
|
61
|
+
ok: results.filter((item) => item.category === "ok").length,
|
|
62
|
+
skipped: results.filter((item) => item.category === "skip").length,
|
|
63
|
+
failed: results.filter((item) => item.category === "fail").length,
|
|
64
|
+
ready: results.filter((item) => item.category === "dry").length
|
|
65
|
+
};
|
|
66
|
+
console.log();
|
|
67
|
+
if (options.dryRun) {
|
|
68
|
+
console.log(`Summary: ready ${counts.ready}, skipped ${counts.skipped}, failed ${counts.failed}.`);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
console.log(`Summary: ok ${counts.ok}, skipped ${counts.skipped}, failed ${counts.failed}.`);
|
|
72
|
+
}
|
|
73
|
+
printDetails(results, options.verbose ?? false);
|
|
74
|
+
return counts.failed > 0 ? 1 : 0;
|
|
75
|
+
}
|
|
76
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
77
|
+
main().then((code) => {
|
|
78
|
+
process.exitCode = code;
|
|
79
|
+
}, (error) => {
|
|
80
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
81
|
+
process.exitCode = 1;
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/defaults.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const DEFAULT_JOBS = 4;
|
|
2
|
+
export const DEFAULT_MAX_DEPTH = 4;
|
|
3
|
+
export const DEFAULT_TIMEOUT_MS = 300_000;
|
|
4
|
+
export const SCAN_IGNORE_NAMES = new Set([
|
|
5
|
+
".build",
|
|
6
|
+
".cache",
|
|
7
|
+
".gradle",
|
|
8
|
+
".idea",
|
|
9
|
+
".mypy_cache",
|
|
10
|
+
".next",
|
|
11
|
+
".nuxt",
|
|
12
|
+
".parcel-cache",
|
|
13
|
+
".pytest_cache",
|
|
14
|
+
".ruff_cache",
|
|
15
|
+
".tox",
|
|
16
|
+
".turbo",
|
|
17
|
+
".venv",
|
|
18
|
+
"__pycache__",
|
|
19
|
+
"build",
|
|
20
|
+
"coverage",
|
|
21
|
+
"DerivedData",
|
|
22
|
+
"dist",
|
|
23
|
+
"env",
|
|
24
|
+
"node_modules",
|
|
25
|
+
"out",
|
|
26
|
+
"Pods",
|
|
27
|
+
"target",
|
|
28
|
+
"vendor",
|
|
29
|
+
"venv"
|
|
30
|
+
]);
|
|
31
|
+
export const SCAN_IGNORE_PREFIXES = ["lab-", "challenge-"];
|
|
32
|
+
//# sourceMappingURL=defaults.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaults.js","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC;AAC9B,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC;AACnC,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC;AAE1C,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC;IACvC,QAAQ;IACR,QAAQ;IACR,SAAS;IACT,OAAO;IACP,aAAa;IACb,OAAO;IACP,OAAO;IACP,eAAe;IACf,eAAe;IACf,aAAa;IACb,MAAM;IACN,QAAQ;IACR,OAAO;IACP,aAAa;IACb,OAAO;IACP,UAAU;IACV,aAAa;IACb,MAAM;IACN,KAAK;IACL,cAAc;IACd,KAAK;IACL,MAAM;IACN,QAAQ;IACR,QAAQ;IACR,MAAM;CACP,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC"}
|
package/dist/discover.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { opendir, realpath, stat } from "node:fs/promises";
|
|
2
|
+
import { relative, resolve, sep } from "node:path";
|
|
3
|
+
import { DEFAULT_MAX_DEPTH, SCAN_IGNORE_NAMES, SCAN_IGNORE_PREFIXES } from "./defaults.js";
|
|
4
|
+
import { repoName, verifyRepo } from "./git.js";
|
|
5
|
+
function shouldIgnoreDir(name) {
|
|
6
|
+
return SCAN_IGNORE_NAMES.has(name) || SCAN_IGNORE_PREFIXES.some((prefix) => name.startsWith(prefix));
|
|
7
|
+
}
|
|
8
|
+
function scanDepth(root, dirPath) {
|
|
9
|
+
const rel = relative(root, dirPath);
|
|
10
|
+
return rel === "" ? 0 : rel.split(sep).length;
|
|
11
|
+
}
|
|
12
|
+
function displayLabel(repoPath, roots) {
|
|
13
|
+
const containingRoots = roots.filter((root) => repoPath === root || repoPath.startsWith(`${root}${sep}`));
|
|
14
|
+
if (containingRoots.length > 0) {
|
|
15
|
+
const root = containingRoots.sort((a, b) => b.length - a.length)[0];
|
|
16
|
+
if (root) {
|
|
17
|
+
const rel = relative(root, repoPath);
|
|
18
|
+
if (rel !== "") {
|
|
19
|
+
return rel;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return repoName(repoPath);
|
|
24
|
+
}
|
|
25
|
+
async function isDirectory(path) {
|
|
26
|
+
try {
|
|
27
|
+
return (await stat(path)).isDirectory();
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function walkForRepos(root, dirPath, options, seen, repoPaths) {
|
|
34
|
+
let dir;
|
|
35
|
+
try {
|
|
36
|
+
dir = await opendir(dirPath);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const childDirs = [];
|
|
42
|
+
let hasGitEntry = false;
|
|
43
|
+
for await (const entry of dir) {
|
|
44
|
+
if (entry.name === ".git" && (entry.isDirectory() || entry.isFile())) {
|
|
45
|
+
hasGitEntry = true;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (!entry.isDirectory() || entry.isSymbolicLink() || shouldIgnoreDir(entry.name)) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
childDirs.push(resolve(dirPath, entry.name));
|
|
52
|
+
}
|
|
53
|
+
if (hasGitEntry) {
|
|
54
|
+
const repoRoot = await verifyRepo(dirPath);
|
|
55
|
+
if (repoRoot && !seen.has(repoRoot)) {
|
|
56
|
+
seen.add(repoRoot);
|
|
57
|
+
repoPaths.push(repoRoot);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (options.maxDepth > 0 && scanDepth(root, dirPath) >= options.maxDepth) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
await Promise.all(childDirs.map((child) => walkForRepos(root, child, options, seen, repoPaths)));
|
|
64
|
+
}
|
|
65
|
+
export async function discoverRepos(rawRoots, options = { maxDepth: DEFAULT_MAX_DEPTH }) {
|
|
66
|
+
const roots = [];
|
|
67
|
+
const missing = [];
|
|
68
|
+
for (const rawRoot of rawRoots) {
|
|
69
|
+
const resolved = resolve(rawRoot);
|
|
70
|
+
if (!(await isDirectory(resolved))) {
|
|
71
|
+
missing.push(rawRoot);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
roots.push(await realpath(resolved));
|
|
75
|
+
}
|
|
76
|
+
const seen = new Set();
|
|
77
|
+
const repoPaths = [];
|
|
78
|
+
await Promise.all(roots.map((root) => walkForRepos(root, root, options, seen, repoPaths)));
|
|
79
|
+
return {
|
|
80
|
+
repos: repoPaths
|
|
81
|
+
.sort((a, b) => a.localeCompare(b))
|
|
82
|
+
.map((path) => ({
|
|
83
|
+
path,
|
|
84
|
+
label: displayLabel(path, roots)
|
|
85
|
+
})),
|
|
86
|
+
missing
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=discover.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discover.js","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"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"}
|
package/dist/git.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { GitCommandResult, Repo, WorktreeStatus } from "./types.js";
|
|
2
|
+
export declare function combinedOutput(result: GitCommandResult): string;
|
|
3
|
+
export declare function firstLine(text: string): string;
|
|
4
|
+
export declare function runGit(repoPath: string, args: string[], timeoutMs?: number): Promise<GitCommandResult>;
|
|
5
|
+
export declare function ok(result: GitCommandResult): boolean;
|
|
6
|
+
export declare function verifyRepo(candidatePath: string): Promise<string | undefined>;
|
|
7
|
+
export declare function parseStatusHeader(header: string): Pick<WorktreeStatus, "branch" | "upstream" | "detached">;
|
|
8
|
+
export declare function inspectWorktree(repo: Repo, timeoutMs: number): Promise<WorktreeStatus>;
|
|
9
|
+
export declare function localBranchExists(repo: Repo, branch: string, timeoutMs: number): Promise<boolean>;
|
|
10
|
+
export declare function remoteBranchExists(repo: Repo, remote: string, branch: string, timeoutMs: number): Promise<boolean>;
|
|
11
|
+
export declare function resolveDefaultBranch(repo: Repo, timeoutMs: number): Promise<string | undefined>;
|
|
12
|
+
export declare function checkoutDefaultBranch(repo: Repo, targetBranch: string, timeoutMs: number): Promise<GitCommandResult>;
|
|
13
|
+
export declare function uniqueBackupBranch(repo: Repo, sourceBranch: string, timeoutMs: number): Promise<string>;
|
|
14
|
+
export declare function commitWorktreeToBackupBranch(repo: Repo, status: WorktreeStatus, timeoutMs: number): Promise<{
|
|
15
|
+
branch: string;
|
|
16
|
+
result: GitCommandResult;
|
|
17
|
+
}>;
|
|
18
|
+
export declare function parkAndCheckoutDefaultBranch(repo: Repo, status: WorktreeStatus, targetBranch: string, timeoutMs: number): Promise<{
|
|
19
|
+
backupBranch?: string;
|
|
20
|
+
result: GitCommandResult;
|
|
21
|
+
}>;
|
|
22
|
+
export declare function classifyPullSuccess(repo: Repo, beforeHead: string, timeoutMs: number): Promise<string>;
|
|
23
|
+
export declare function repoName(repoPath: string): string;
|
package/dist/git.js
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { basename } from "node:path";
|
|
3
|
+
import { DEFAULT_TIMEOUT_MS } from "./defaults.js";
|
|
4
|
+
export function combinedOutput(result) {
|
|
5
|
+
return [result.stdout.trim(), result.stderr.trim()].filter(Boolean).join("\n");
|
|
6
|
+
}
|
|
7
|
+
export function firstLine(text) {
|
|
8
|
+
return text
|
|
9
|
+
.split(/\r?\n/)
|
|
10
|
+
.map((line) => line.trim())
|
|
11
|
+
.find(Boolean) ?? "";
|
|
12
|
+
}
|
|
13
|
+
export function runGit(repoPath, args, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
14
|
+
return new Promise((resolve) => {
|
|
15
|
+
execFile("git", ["-C", repoPath, ...args], {
|
|
16
|
+
encoding: "utf8",
|
|
17
|
+
env: {
|
|
18
|
+
...process.env,
|
|
19
|
+
GIT_TERMINAL_PROMPT: process.env.GIT_TERMINAL_PROMPT ?? "0"
|
|
20
|
+
},
|
|
21
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
22
|
+
timeout: timeoutMs
|
|
23
|
+
}, (error, stdout, stderr) => {
|
|
24
|
+
if (!error) {
|
|
25
|
+
resolve({ code: 0, stdout, stderr, timedOut: false });
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const nodeError = error;
|
|
29
|
+
const code = typeof nodeError.code === "number" ? nodeError.code : nodeError.killed ? 124 : 1;
|
|
30
|
+
resolve({
|
|
31
|
+
code,
|
|
32
|
+
stdout,
|
|
33
|
+
stderr,
|
|
34
|
+
timedOut: nodeError.killed === true || nodeError.signal === "SIGTERM"
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
export function ok(result) {
|
|
40
|
+
return result.code === 0 && !result.timedOut;
|
|
41
|
+
}
|
|
42
|
+
export async function verifyRepo(candidatePath) {
|
|
43
|
+
const result = await runGit(candidatePath, ["rev-parse", "--show-toplevel"], 30_000);
|
|
44
|
+
if (!ok(result)) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
const root = result.stdout.trim();
|
|
48
|
+
return root.length > 0 ? root : undefined;
|
|
49
|
+
}
|
|
50
|
+
export function parseStatusHeader(header) {
|
|
51
|
+
const branchText = header.replace(/^##\s*/, "").trim();
|
|
52
|
+
if (branchText.startsWith("HEAD ")) {
|
|
53
|
+
return { branch: "detached", detached: true };
|
|
54
|
+
}
|
|
55
|
+
if (branchText.startsWith("No commits yet on ")) {
|
|
56
|
+
const branch = branchText.replace(/^No commits yet on\s*/, "").trim();
|
|
57
|
+
return { branch: branch || "-", detached: false };
|
|
58
|
+
}
|
|
59
|
+
if (branchText.includes("...")) {
|
|
60
|
+
const [branch = "-", upstreamText = ""] = branchText.split("...", 2);
|
|
61
|
+
const upstream = upstreamText.split(" [", 1)[0]?.trim();
|
|
62
|
+
const parsed = {
|
|
63
|
+
branch: branch.trim() || "-",
|
|
64
|
+
detached: false
|
|
65
|
+
};
|
|
66
|
+
if (upstream) {
|
|
67
|
+
parsed.upstream = upstream;
|
|
68
|
+
}
|
|
69
|
+
return parsed;
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
branch: branchText.split(" [", 1)[0]?.trim() || "-",
|
|
73
|
+
detached: false
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export async function inspectWorktree(repo, timeoutMs) {
|
|
77
|
+
const result = await runGit(repo.path, ["status", "--porcelain=v1", "--branch"], timeoutMs);
|
|
78
|
+
if (!ok(result)) {
|
|
79
|
+
return {
|
|
80
|
+
branch: "-",
|
|
81
|
+
detached: false,
|
|
82
|
+
dirty: false,
|
|
83
|
+
dirtyText: "",
|
|
84
|
+
error: firstLine(combinedOutput(result)) || "status failed"
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const lines = result.stdout.split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
88
|
+
const header = lines[0];
|
|
89
|
+
if (!header?.startsWith("## ")) {
|
|
90
|
+
return {
|
|
91
|
+
branch: "-",
|
|
92
|
+
detached: false,
|
|
93
|
+
dirty: false,
|
|
94
|
+
dirtyText: "",
|
|
95
|
+
error: "unable to read status"
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
const parsed = parseStatusHeader(header);
|
|
99
|
+
const dirtyCount = lines.length - 1;
|
|
100
|
+
return {
|
|
101
|
+
...parsed,
|
|
102
|
+
dirty: dirtyCount > 0,
|
|
103
|
+
dirtyText: dirtyCount > 0 ? `${dirtyCount} change${dirtyCount === 1 ? "" : "s"}` : ""
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
export async function localBranchExists(repo, branch, timeoutMs) {
|
|
107
|
+
const result = await runGit(repo.path, ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`], timeoutMs);
|
|
108
|
+
return ok(result);
|
|
109
|
+
}
|
|
110
|
+
export async function remoteBranchExists(repo, remote, branch, timeoutMs) {
|
|
111
|
+
const result = await runGit(repo.path, ["show-ref", "--verify", "--quiet", `refs/remotes/${remote}/${branch}`], timeoutMs);
|
|
112
|
+
return ok(result);
|
|
113
|
+
}
|
|
114
|
+
export async function resolveDefaultBranch(repo, timeoutMs) {
|
|
115
|
+
const originHead = await runGit(repo.path, ["symbolic-ref", "--quiet", "--short", "refs/remotes/origin/HEAD"], timeoutMs);
|
|
116
|
+
if (ok(originHead)) {
|
|
117
|
+
const ref = originHead.stdout.trim();
|
|
118
|
+
if (ref.startsWith("origin/")) {
|
|
119
|
+
return ref.slice("origin/".length);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
for (const branch of ["main", "master"]) {
|
|
123
|
+
if (await localBranchExists(repo, branch, timeoutMs)) {
|
|
124
|
+
return branch;
|
|
125
|
+
}
|
|
126
|
+
if (await remoteBranchExists(repo, "origin", branch, timeoutMs)) {
|
|
127
|
+
return branch;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
export async function checkoutDefaultBranch(repo, targetBranch, timeoutMs) {
|
|
133
|
+
if (await localBranchExists(repo, targetBranch, timeoutMs)) {
|
|
134
|
+
return runGit(repo.path, ["checkout", targetBranch], timeoutMs);
|
|
135
|
+
}
|
|
136
|
+
if (await remoteBranchExists(repo, "origin", targetBranch, timeoutMs)) {
|
|
137
|
+
return runGit(repo.path, ["checkout", "-B", targetBranch, `origin/${targetBranch}`], timeoutMs);
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
code: 1,
|
|
141
|
+
stdout: "",
|
|
142
|
+
stderr: `default branch not found: ${targetBranch}`,
|
|
143
|
+
timedOut: false
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function branchNameFragment(value) {
|
|
147
|
+
const fragment = value.replace(/[^a-zA-Z0-9_.-]+/g, "-").replace(/^[-_.]+|[-_.]+$/g, "");
|
|
148
|
+
return (fragment || "worktree").slice(0, 48);
|
|
149
|
+
}
|
|
150
|
+
export async function uniqueBackupBranch(repo, sourceBranch, timeoutMs) {
|
|
151
|
+
const timestamp = new Date()
|
|
152
|
+
.toISOString()
|
|
153
|
+
.replace(/[-:]/g, "")
|
|
154
|
+
.replace(/\..+$/, "")
|
|
155
|
+
.replace("T", "-");
|
|
156
|
+
const base = `multipull-backup/${timestamp}-${branchNameFragment(sourceBranch)}`;
|
|
157
|
+
let candidate = base;
|
|
158
|
+
let suffix = 2;
|
|
159
|
+
while (await localBranchExists(repo, candidate, timeoutMs)) {
|
|
160
|
+
candidate = `${base}-${suffix}`;
|
|
161
|
+
suffix += 1;
|
|
162
|
+
}
|
|
163
|
+
return candidate;
|
|
164
|
+
}
|
|
165
|
+
export async function commitWorktreeToBackupBranch(repo, status, timeoutMs) {
|
|
166
|
+
const source = status.detached ? "detached" : status.branch;
|
|
167
|
+
const branch = await uniqueBackupBranch(repo, source, timeoutMs);
|
|
168
|
+
const outputs = [];
|
|
169
|
+
for (const args of [
|
|
170
|
+
["checkout", "-b", branch],
|
|
171
|
+
["add", "-A"],
|
|
172
|
+
["commit", "--no-verify", "-m", `multipull backup before switching from ${source}`]
|
|
173
|
+
]) {
|
|
174
|
+
const result = await runGit(repo.path, args, timeoutMs);
|
|
175
|
+
outputs.push(combinedOutput(result));
|
|
176
|
+
if (!ok(result)) {
|
|
177
|
+
return {
|
|
178
|
+
branch,
|
|
179
|
+
result: {
|
|
180
|
+
...result,
|
|
181
|
+
stdout: outputs.filter(Boolean).join("\n")
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
branch,
|
|
188
|
+
result: {
|
|
189
|
+
code: 0,
|
|
190
|
+
stdout: outputs.filter(Boolean).join("\n"),
|
|
191
|
+
stderr: "",
|
|
192
|
+
timedOut: false
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
export async function parkAndCheckoutDefaultBranch(repo, status, targetBranch, timeoutMs) {
|
|
197
|
+
const outputs = [];
|
|
198
|
+
let backupBranch;
|
|
199
|
+
if (status.dirty) {
|
|
200
|
+
const backup = await commitWorktreeToBackupBranch(repo, status, timeoutMs);
|
|
201
|
+
backupBranch = backup.branch;
|
|
202
|
+
outputs.push(combinedOutput(backup.result));
|
|
203
|
+
if (!ok(backup.result)) {
|
|
204
|
+
return {
|
|
205
|
+
backupBranch,
|
|
206
|
+
result: {
|
|
207
|
+
...backup.result,
|
|
208
|
+
stdout: outputs.filter(Boolean).join("\n")
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const checkout = await checkoutDefaultBranch(repo, targetBranch, timeoutMs);
|
|
214
|
+
outputs.push(combinedOutput(checkout));
|
|
215
|
+
const parkedResult = {
|
|
216
|
+
result: {
|
|
217
|
+
...checkout,
|
|
218
|
+
stdout: outputs.filter(Boolean).join("\n")
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
if (backupBranch) {
|
|
222
|
+
parkedResult.backupBranch = backupBranch;
|
|
223
|
+
}
|
|
224
|
+
return parkedResult;
|
|
225
|
+
}
|
|
226
|
+
export async function classifyPullSuccess(repo, beforeHead, timeoutMs) {
|
|
227
|
+
const after = await runGit(repo.path, ["rev-parse", "HEAD"], timeoutMs);
|
|
228
|
+
if (ok(after) && after.stdout.trim() !== beforeHead) {
|
|
229
|
+
return "OK updated";
|
|
230
|
+
}
|
|
231
|
+
return "OK current";
|
|
232
|
+
}
|
|
233
|
+
export function repoName(repoPath) {
|
|
234
|
+
return basename(repoPath) || repoPath;
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=git.js.map
|
package/dist/git.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"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"}
|
package/dist/runner.d.ts
ADDED
package/dist/runner.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { DEFAULT_JOBS, DEFAULT_TIMEOUT_MS } from "./defaults.js";
|
|
2
|
+
import { classifyPullSuccess, combinedOutput, firstLine, inspectWorktree, ok, parkAndCheckoutDefaultBranch, resolveDefaultBranch, runGit } from "./git.js";
|
|
3
|
+
function pendingResult(repo) {
|
|
4
|
+
return {
|
|
5
|
+
repo,
|
|
6
|
+
branch: "-",
|
|
7
|
+
upstream: "-",
|
|
8
|
+
result: "PENDING",
|
|
9
|
+
category: "pending"
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function failedResult(repo, error) {
|
|
13
|
+
return {
|
|
14
|
+
repo,
|
|
15
|
+
branch: "-",
|
|
16
|
+
upstream: "-",
|
|
17
|
+
result: "FAIL internal error",
|
|
18
|
+
category: "fail",
|
|
19
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export async function processRepo(repo, options) {
|
|
23
|
+
const result = pendingResult(repo);
|
|
24
|
+
let switchTarget = "";
|
|
25
|
+
let switchAction = "";
|
|
26
|
+
let switchOutput = "";
|
|
27
|
+
let status = await inspectWorktree(repo, options.timeoutMs);
|
|
28
|
+
result.branch = status.branch;
|
|
29
|
+
if (status.error) {
|
|
30
|
+
return {
|
|
31
|
+
...result,
|
|
32
|
+
result: "FAIL status",
|
|
33
|
+
category: "fail",
|
|
34
|
+
detail: status.error
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (options.parkToDefaultBranch) {
|
|
38
|
+
const targetBranch = await resolveDefaultBranch(repo, options.timeoutMs);
|
|
39
|
+
if (!targetBranch) {
|
|
40
|
+
return {
|
|
41
|
+
...result,
|
|
42
|
+
result: "FAIL default branch",
|
|
43
|
+
category: "fail",
|
|
44
|
+
detail: "no default branch found"
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const shouldSwitch = status.detached || status.branch !== targetBranch;
|
|
48
|
+
if (shouldSwitch) {
|
|
49
|
+
switchTarget = targetBranch;
|
|
50
|
+
switchAction = status.dirty ? "parked" : "switched";
|
|
51
|
+
if (options.dryRun) {
|
|
52
|
+
return {
|
|
53
|
+
...result,
|
|
54
|
+
result: `READY ${switchAction} -> ${switchTarget}`,
|
|
55
|
+
category: "dry"
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const switchResult = await parkAndCheckoutDefaultBranch(repo, status, targetBranch, options.timeoutMs);
|
|
59
|
+
switchOutput = combinedOutput(switchResult.result);
|
|
60
|
+
if (!ok(switchResult.result)) {
|
|
61
|
+
return {
|
|
62
|
+
...result,
|
|
63
|
+
result: `FAIL ${switchAction} -> ${switchTarget}`,
|
|
64
|
+
category: "fail",
|
|
65
|
+
detail: firstLine(switchOutput) || "switch failed",
|
|
66
|
+
output: switchOutput
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
status = await inspectWorktree(repo, options.timeoutMs);
|
|
70
|
+
result.branch = status.branch;
|
|
71
|
+
if (switchResult.backupBranch) {
|
|
72
|
+
result.detail = `backup branch: ${switchResult.backupBranch}`;
|
|
73
|
+
}
|
|
74
|
+
if (status.error) {
|
|
75
|
+
return {
|
|
76
|
+
...result,
|
|
77
|
+
result: "FAIL status",
|
|
78
|
+
category: "fail",
|
|
79
|
+
detail: status.error,
|
|
80
|
+
output: switchOutput
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (status.detached) {
|
|
86
|
+
return {
|
|
87
|
+
...result,
|
|
88
|
+
result: "SKIP detached HEAD",
|
|
89
|
+
category: "skip"
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
if (status.dirty) {
|
|
93
|
+
return {
|
|
94
|
+
...result,
|
|
95
|
+
result: `SKIP dirty (${status.dirtyText})`,
|
|
96
|
+
category: "skip"
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (!status.upstream) {
|
|
100
|
+
return {
|
|
101
|
+
...result,
|
|
102
|
+
result: "SKIP no upstream",
|
|
103
|
+
category: "skip"
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
result.upstream = status.upstream;
|
|
107
|
+
if (options.dryRun) {
|
|
108
|
+
return {
|
|
109
|
+
...result,
|
|
110
|
+
result: "READY pull",
|
|
111
|
+
category: "dry"
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
const before = await runGit(repo.path, ["rev-parse", "HEAD"], options.timeoutMs);
|
|
115
|
+
if (!ok(before)) {
|
|
116
|
+
const output = combinedOutput(before);
|
|
117
|
+
return {
|
|
118
|
+
...result,
|
|
119
|
+
result: "FAIL read HEAD",
|
|
120
|
+
category: "fail",
|
|
121
|
+
detail: firstLine(output),
|
|
122
|
+
output
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const pull = await runGit(repo.path, ["pull", "--ff-only", "--prune"], options.timeoutMs);
|
|
126
|
+
const pullOutput = [switchOutput, combinedOutput(pull)].filter(Boolean).join("\n");
|
|
127
|
+
if (!ok(pull)) {
|
|
128
|
+
return {
|
|
129
|
+
...result,
|
|
130
|
+
result: "FAIL pull",
|
|
131
|
+
category: "fail",
|
|
132
|
+
detail: pull.timedOut ? `timed out after ${Math.round(options.timeoutMs / 1000)}s` : firstLine(combinedOutput(pull)),
|
|
133
|
+
output: pullOutput
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
const pullResult = await classifyPullSuccess(repo, before.stdout.trim(), options.timeoutMs);
|
|
137
|
+
return {
|
|
138
|
+
...result,
|
|
139
|
+
result: switchTarget ? `${pullResult} (${switchAction} -> ${switchTarget})` : pullResult,
|
|
140
|
+
category: "ok",
|
|
141
|
+
output: pullOutput
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
export async function runRepos(repos, options = {}) {
|
|
145
|
+
const runOptions = {
|
|
146
|
+
dryRun: options.dryRun ?? false,
|
|
147
|
+
verbose: options.verbose ?? false,
|
|
148
|
+
parkToDefaultBranch: options.parkToDefaultBranch ?? false,
|
|
149
|
+
jobs: options.jobs ?? DEFAULT_JOBS,
|
|
150
|
+
timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS
|
|
151
|
+
};
|
|
152
|
+
const results = new Map();
|
|
153
|
+
const queue = [...repos];
|
|
154
|
+
async function worker() {
|
|
155
|
+
while (queue.length > 0) {
|
|
156
|
+
const repo = queue.shift();
|
|
157
|
+
if (!repo) {
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
results.set(repo.path, await processRepo(repo, runOptions));
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
results.set(repo.path, failedResult(repo, error));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
await Promise.all(Array.from({ length: Math.min(runOptions.jobs, repos.length) }, () => worker()));
|
|
169
|
+
return repos.map((repo) => results.get(repo.path) ?? failedResult(repo, "missing result"));
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
|
package/dist/table.d.ts
ADDED
package/dist/table.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
function fit(text, width) {
|
|
2
|
+
if (text.length <= width) {
|
|
3
|
+
return text.padEnd(width, " ");
|
|
4
|
+
}
|
|
5
|
+
if (width <= 3) {
|
|
6
|
+
return text.slice(0, width);
|
|
7
|
+
}
|
|
8
|
+
return `${text.slice(0, width - 3)}...`;
|
|
9
|
+
}
|
|
10
|
+
function widths(results) {
|
|
11
|
+
const rows = [
|
|
12
|
+
["Repo", "Branch", "Upstream", "Result"],
|
|
13
|
+
...results.map((item) => [item.repo.label, item.branch, item.upstream, item.result])
|
|
14
|
+
];
|
|
15
|
+
const maxes = [0, 1, 2, 3].map((index) => Math.max(...rows.map((row) => row[index]?.length ?? 0)));
|
|
16
|
+
const branch = Math.min(Math.max(maxes[1] ?? 8, 8), 20);
|
|
17
|
+
const upstream = Math.min(Math.max(maxes[2] ?? 12, 12), 28);
|
|
18
|
+
const result = Math.min(Math.max(maxes[3] ?? 16, 16), 40);
|
|
19
|
+
const terminal = process.stdout.columns ?? 120;
|
|
20
|
+
const repo = Math.max(16, Math.min(maxes[0] ?? 16, terminal - branch - upstream - result - 6));
|
|
21
|
+
return [repo, branch, upstream, result];
|
|
22
|
+
}
|
|
23
|
+
export function renderTable(results) {
|
|
24
|
+
const headers = ["Repo", "Branch", "Upstream", "Result"];
|
|
25
|
+
const columnWidths = widths(results);
|
|
26
|
+
const lines = [
|
|
27
|
+
headers.map((header, index) => fit(header, columnWidths[index] ?? header.length)).join(" "),
|
|
28
|
+
columnWidths.map((width) => "-".repeat(width)).join(" ")
|
|
29
|
+
];
|
|
30
|
+
for (const item of results) {
|
|
31
|
+
const row = [item.repo.label, item.branch, item.upstream, item.result];
|
|
32
|
+
lines.push(row.map((cell, index) => fit(cell, columnWidths[index] ?? cell.length)).join(" "));
|
|
33
|
+
}
|
|
34
|
+
return lines.join("\n");
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table.js","sourceRoot":"","sources":["../src/table.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export type ResultCategory = "ok" | "skip" | "fail" | "dry" | "pending" | "running";
|
|
2
|
+
export interface Repo {
|
|
3
|
+
path: string;
|
|
4
|
+
label: string;
|
|
5
|
+
}
|
|
6
|
+
export interface GitCommandResult {
|
|
7
|
+
code: number;
|
|
8
|
+
stdout: string;
|
|
9
|
+
stderr: string;
|
|
10
|
+
timedOut: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface WorktreeStatus {
|
|
13
|
+
branch: string;
|
|
14
|
+
upstream?: string;
|
|
15
|
+
detached: boolean;
|
|
16
|
+
dirty: boolean;
|
|
17
|
+
dirtyText: string;
|
|
18
|
+
error?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface RepoResult {
|
|
21
|
+
repo: Repo;
|
|
22
|
+
branch: string;
|
|
23
|
+
upstream: string;
|
|
24
|
+
result: string;
|
|
25
|
+
category: ResultCategory;
|
|
26
|
+
detail?: string;
|
|
27
|
+
output?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface RunOptions {
|
|
30
|
+
dryRun: boolean;
|
|
31
|
+
verbose: boolean;
|
|
32
|
+
parkToDefaultBranch: boolean;
|
|
33
|
+
jobs: number;
|
|
34
|
+
timeoutMs: number;
|
|
35
|
+
}
|
|
36
|
+
export interface DiscoverOptions {
|
|
37
|
+
maxDepth: number;
|
|
38
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@huhuhang/multipull",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Pull many Git repositories safely from one CLI.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/huhuhang/multipull.git"
|
|
9
|
+
},
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/huhuhang/multipull/issues"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/huhuhang/multipull#readme",
|
|
14
|
+
"bin": {
|
|
15
|
+
"multipull": "./dist/cli.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc -p tsconfig.json",
|
|
24
|
+
"dev": "tsx src/cli.ts",
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
27
|
+
"check": "npm run typecheck && npm run test && npm run build",
|
|
28
|
+
"prepack": "npm run build"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"git",
|
|
32
|
+
"pull",
|
|
33
|
+
"cli",
|
|
34
|
+
"monorepo",
|
|
35
|
+
"repositories"
|
|
36
|
+
],
|
|
37
|
+
"author": "huhuhang",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=20"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"commander": "^14.0.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^24.0.0",
|
|
50
|
+
"tsx": "^4.20.0",
|
|
51
|
+
"typescript": "^5.8.0",
|
|
52
|
+
"vitest": "^4.1.9"
|
|
53
|
+
}
|
|
54
|
+
}
|