@openagt/skill-branches 0.0.1
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/SKILL.md +70 -0
- package/bin/branches +11 -0
- package/dist/bin-dir.d.ts +7 -0
- package/dist/bin-dir.d.ts.map +1 -0
- package/dist/bin-dir.js +8 -0
- package/dist/bin-dir.js.map +1 -0
- package/dist/branch-links.d.ts +37 -0
- package/dist/branch-links.d.ts.map +1 -0
- package/dist/branch-links.js +67 -0
- package/dist/branch-links.js.map +1 -0
- package/dist/branch-names.d.ts +41 -0
- package/dist/branch-names.d.ts.map +1 -0
- package/dist/branch-names.js +54 -0
- package/dist/branch-names.js.map +1 -0
- package/dist/branch-state.d.ts +68 -0
- package/dist/branch-state.d.ts.map +1 -0
- package/dist/branch-state.js +146 -0
- package/dist/branch-state.js.map +1 -0
- package/dist/checkout.d.ts +25 -0
- package/dist/checkout.d.ts.map +1 -0
- package/dist/checkout.js +37 -0
- package/dist/checkout.js.map +1 -0
- package/dist/cli.d.ts +36 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +310 -0
- package/dist/cli.js.map +1 -0
- package/dist/command-link.d.ts +20 -0
- package/dist/command-link.d.ts.map +1 -0
- package/dist/command-link.js +37 -0
- package/dist/command-link.js.map +1 -0
- package/dist/file-branch.d.ts +150 -0
- package/dist/file-branch.d.ts.map +1 -0
- package/dist/file-branch.js +371 -0
- package/dist/file-branch.js.map +1 -0
- package/dist/git-exclude.d.ts +17 -0
- package/dist/git-exclude.d.ts.map +1 -0
- package/dist/git-exclude.js +30 -0
- package/dist/git-exclude.js.map +1 -0
- package/dist/git.d.ts +73 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +164 -0
- package/dist/git.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/merge-hold.d.ts +20 -0
- package/dist/merge-hold.d.ts.map +1 -0
- package/dist/merge-hold.js +64 -0
- package/dist/merge-hold.js.map +1 -0
- package/dist/merge-watch.d.ts +68 -0
- package/dist/merge-watch.d.ts.map +1 -0
- package/dist/merge-watch.js +97 -0
- package/dist/merge-watch.js.map +1 -0
- package/dist/merge.d.ts +70 -0
- package/dist/merge.d.ts.map +1 -0
- package/dist/merge.js +55 -0
- package/dist/merge.js.map +1 -0
- package/dist/publish.d.ts +138 -0
- package/dist/publish.d.ts.map +1 -0
- package/dist/publish.js +215 -0
- package/dist/publish.js.map +1 -0
- package/dist/push.d.ts +53 -0
- package/dist/push.d.ts.map +1 -0
- package/dist/push.js +41 -0
- package/dist/push.js.map +1 -0
- package/dist/reclaim.d.ts +74 -0
- package/dist/reclaim.d.ts.map +1 -0
- package/dist/reclaim.js +87 -0
- package/dist/reclaim.js.map +1 -0
- package/dist/skill-links.d.ts +43 -0
- package/dist/skill-links.d.ts.map +1 -0
- package/dist/skill-links.js +50 -0
- package/dist/skill-links.js.map +1 -0
- package/dist/worktree-deps.d.ts +30 -0
- package/dist/worktree-deps.d.ts.map +1 -0
- package/dist/worktree-deps.js +128 -0
- package/dist/worktree-deps.js.map +1 -0
- package/dist/worktree.d.ts +220 -0
- package/dist/worktree.d.ts.map +1 -0
- package/dist/worktree.js +397 -0
- package/dist/worktree.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-branch.d.ts","sourceRoot":"","sources":["../src/file-branch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
import { dirname, join } from 'node:path';
|
|
2
|
+
import { BRANCHES_DIR } from './branch-names.js';
|
|
3
|
+
import { nodeGitRunner } from './git.js';
|
|
4
|
+
import { excludeFromGit } from './git-exclude.js';
|
|
5
|
+
// A branch used as a file store: a branch of the project's repository that holds files nobody
|
|
6
|
+
// edits in a working tree — the way `gh-pages` holds a site — read and written by programs, and
|
|
7
|
+
// safe to push and pull eagerly because nothing on it is anyone's checkout. The caller names the
|
|
8
|
+
// branch; this module knows git, not what the files mean.
|
|
9
|
+
//
|
|
10
|
+
// Two writers, one rule. A long-lived process (a daemon) keeps a persistent checkout of the
|
|
11
|
+
// branch under `.branches/<branch>` and writes through {@link withFileBranch}: one serialized
|
|
12
|
+
// cycle per branch — sync, apply, commit, push. A one-shot writer in any clone (a command an
|
|
13
|
+
// agent runs) writes through {@link writeFileBranchDetached}: a throwaway worktree on origin's
|
|
14
|
+
// tip, the same apply-commit-push, gone afterwards — it never touches the persistent checkout,
|
|
15
|
+
// which is another process's. Both treat the change as an intent: when the push loses a race,
|
|
16
|
+
// the cycle re-syncs and re-applies rather than force-fitting a stale commit.
|
|
17
|
+
/** The persistent checkout of `branch` under a project: `<repo>/.branches/<branch>`. */
|
|
18
|
+
export function fileBranchPath(repo, branch) {
|
|
19
|
+
return join(repo, BRANCHES_DIR, branch);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* git's well-known empty tree, present in every repository without being written first. Committing
|
|
23
|
+
* it is how a branch is born parentless with no working-tree gymnastics: `git commit-tree` +
|
|
24
|
+
* `git branch` never touches any checkout, and no code commit is ever an ancestor of the history.
|
|
25
|
+
*/
|
|
26
|
+
const EMPTY_TREE = '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
|
|
27
|
+
function resolveDeps(deps) {
|
|
28
|
+
return { git: deps.git ?? nodeGitRunner(), log: deps.log ?? (() => { }) };
|
|
29
|
+
}
|
|
30
|
+
/** Whether the repo has any remote to sync the branch with. */
|
|
31
|
+
async function hasRemote(cwd, git) {
|
|
32
|
+
try {
|
|
33
|
+
return (await git(['remote'], cwd)).trim().length > 0;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** Whether a ref exists locally. */
|
|
40
|
+
async function refExists(cwd, ref, git) {
|
|
41
|
+
return git(['rev-parse', '--verify', '--quiet', ref], cwd).then(() => true, () => false);
|
|
42
|
+
}
|
|
43
|
+
/** The message a failed git invocation carries, or the value itself as text. */
|
|
44
|
+
function errorMessage(err) {
|
|
45
|
+
return err instanceof Error ? err.message : String(err);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* One serialized cycle per branch of a project, so a writer and the eager pull can never
|
|
49
|
+
* interleave: the promise chain is the lock, and every public entry point below joins it.
|
|
50
|
+
*/
|
|
51
|
+
const chains = new Map();
|
|
52
|
+
function serialize(repo, branch, task) {
|
|
53
|
+
const key = `${repo}\0${branch}`;
|
|
54
|
+
const next = (chains.get(key) ?? Promise.resolve()).then(task, task);
|
|
55
|
+
chains.set(key, next.catch(() => { }));
|
|
56
|
+
return next;
|
|
57
|
+
}
|
|
58
|
+
/** Make the branch exist locally: origin's copy when there is one, else born here, parentless. */
|
|
59
|
+
async function ensureBranchRef(repo, branch, git) {
|
|
60
|
+
if (await refExists(repo, `refs/heads/${branch}`, git))
|
|
61
|
+
return;
|
|
62
|
+
if (await hasRemote(repo, git))
|
|
63
|
+
await git(['fetch', 'origin', branch], repo).catch(() => { });
|
|
64
|
+
if (await refExists(repo, `refs/remotes/origin/${branch}`, git)) {
|
|
65
|
+
await git(['branch', branch, `origin/${branch}`], repo);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
const commit = (await git(['commit-tree', EMPTY_TREE, '-m', `create the ${branch} branch`], repo)).trim();
|
|
69
|
+
await git(['branch', branch, commit], repo);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The unserialized ensure: branch and checkout. Split from {@link ensureFileBranch} so the
|
|
74
|
+
* writer and the pull can run it inside the cycle they already hold the chain for.
|
|
75
|
+
*/
|
|
76
|
+
async function ensureCore(repo, branch, r) {
|
|
77
|
+
const path = fileBranchPath(repo, branch);
|
|
78
|
+
// Already checked out on the right branch: done. The common case, taken on every tick.
|
|
79
|
+
const onBranch = await r.git(['rev-parse', '--abbrev-ref', 'HEAD'], path).then(out => out.trim() === branch, () => false);
|
|
80
|
+
if (onBranch)
|
|
81
|
+
return;
|
|
82
|
+
await ensureBranchRef(repo, branch, r.git);
|
|
83
|
+
// A stale registration at this path (the dir was deleted by hand) blocks the add.
|
|
84
|
+
await r.git(['worktree', 'prune'], repo).catch(() => { });
|
|
85
|
+
await r.git(['worktree', 'add', path, branch], repo);
|
|
86
|
+
// The checkout sits under `.branches/` like the agent checkouts, and is hidden the same way
|
|
87
|
+
// (git's own exclude, no tracked file) — this may be the first checkout the project gets.
|
|
88
|
+
await excludeFromGit(repo, '/' + BRANCHES_DIR, undefined, r.git).catch(() => { });
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Bring the checkout up to date with origin: fetch, then rebase whatever local commits exist (a
|
|
92
|
+
* push that could not land earlier) onto origin's tip. A conflict resolves toward origin — the
|
|
93
|
+
* writer re-applies the local intent afterwards, which is the "re-run on conflict" rule the whole
|
|
94
|
+
* cycle is built on.
|
|
95
|
+
*/
|
|
96
|
+
async function syncCore(repo, branch, r) {
|
|
97
|
+
if (!(await hasRemote(repo, r.git)))
|
|
98
|
+
return;
|
|
99
|
+
const path = fileBranchPath(repo, branch);
|
|
100
|
+
await r.git(['fetch', 'origin', branch], repo).catch(() => { });
|
|
101
|
+
if (!(await refExists(repo, `refs/remotes/origin/${branch}`, r.git)))
|
|
102
|
+
return;
|
|
103
|
+
try {
|
|
104
|
+
await r.git(['rebase', `origin/${branch}`], path);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
await r.git(['rebase', '--abort'], path).catch(() => { });
|
|
108
|
+
await r.git(['reset', '--hard', `origin/${branch}`], path);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Make sure the branch and its persistent checkout exist. Idempotent and cheap when everything is
|
|
113
|
+
* in place (one `git rev-parse` against the checkout); never throws — a project this cannot be
|
|
114
|
+
* set up in reports why and is left alone.
|
|
115
|
+
*/
|
|
116
|
+
export async function ensureFileBranch(repo, branch, deps = {}) {
|
|
117
|
+
const r = resolveDeps(deps);
|
|
118
|
+
return serialize(repo, branch, async () => {
|
|
119
|
+
try {
|
|
120
|
+
await ensureCore(repo, branch, r);
|
|
121
|
+
return { ok: true };
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
return { ok: false, error: errorMessage(err) };
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
function resolveMessage(message) {
|
|
129
|
+
return typeof message === 'function' ? message() : message;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Apply one change to the branch through its persistent checkout: sync with origin, run `op`
|
|
133
|
+
* against the checkout, commit whatever it changed, push. The single funnel a long-lived
|
|
134
|
+
* process's writes go through.
|
|
135
|
+
*
|
|
136
|
+
* `op` must be re-runnable: when the push loses a race with another writer, the cycle re-syncs
|
|
137
|
+
* and runs it again against the fresher state rather than force-fitting a stale commit — the op
|
|
138
|
+
* *is* the intent, the commit is just its serialization. Two attempts; a push that still fails
|
|
139
|
+
* (the network, most likely) keeps the commit local and reports the error — the next cycle's sync
|
|
140
|
+
* rebases it onto whatever origin has by then, and the next push carries it.
|
|
141
|
+
*
|
|
142
|
+
* Never throws: callers run on background ticks with nothing to catch it.
|
|
143
|
+
*/
|
|
144
|
+
export async function withFileBranch(repo, branch, message, op, deps = {}) {
|
|
145
|
+
const r = resolveDeps(deps);
|
|
146
|
+
const path = fileBranchPath(repo, branch);
|
|
147
|
+
return serialize(repo, branch, async () => {
|
|
148
|
+
try {
|
|
149
|
+
await ensureCore(repo, branch, r);
|
|
150
|
+
const remote = await hasRemote(repo, r.git);
|
|
151
|
+
for (let attempt = 0;; attempt++) {
|
|
152
|
+
await syncCore(repo, branch, r);
|
|
153
|
+
// The tip before this op's commit: what a lost push winds back to before re-applying, so
|
|
154
|
+
// the op's first serialization is dropped rather than rebased under its second run.
|
|
155
|
+
const before = (await r.git(['rev-parse', 'HEAD'], path)).trim();
|
|
156
|
+
await op(path);
|
|
157
|
+
await r.git(['add', '-A'], path);
|
|
158
|
+
const staged = (await r.git(['status', '--porcelain'], path)).trim();
|
|
159
|
+
if (staged)
|
|
160
|
+
await r.git(['commit', '-m', resolveMessage(message)], path);
|
|
161
|
+
if (!remote)
|
|
162
|
+
return { ok: true, changed: Boolean(staged), pushed: false };
|
|
163
|
+
// Unpushed commits — this cycle's, or an earlier cycle's that the sync just rebased. The
|
|
164
|
+
// push is owed whenever any exist, even when this op itself wrote nothing new.
|
|
165
|
+
const ahead = (await refExists(repo, `refs/remotes/origin/${branch}`, r.git))
|
|
166
|
+
? (await r.git(['rev-list', '--count', `origin/${branch}..${branch}`], repo)).trim() !== '0'
|
|
167
|
+
: true;
|
|
168
|
+
if (!ahead)
|
|
169
|
+
return { ok: true, changed: false, pushed: false };
|
|
170
|
+
try {
|
|
171
|
+
await r.git(['push', 'origin', `${branch}:${branch}`], path);
|
|
172
|
+
return { ok: true, changed: Boolean(staged), pushed: true };
|
|
173
|
+
}
|
|
174
|
+
catch (err) {
|
|
175
|
+
if (attempt >= 1)
|
|
176
|
+
return { ok: false, committed: true, error: `the ${branch} branch could not be pushed: ${errorMessage(err)}` };
|
|
177
|
+
if (staged)
|
|
178
|
+
await r.git(['reset', '--hard', before], path);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
// The op's half-written files must not ride a later, unrelated commit: put the checkout
|
|
184
|
+
// back to its committed state before reporting.
|
|
185
|
+
await r.git(['reset', '--hard'], path).catch(() => { });
|
|
186
|
+
await r.git(['clean', '-fd'], path).catch(() => { });
|
|
187
|
+
return { ok: false, committed: false, error: errorMessage(err) };
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* The eager pull: sync the persistent checkout with origin so this machine reads what other
|
|
193
|
+
* machines and cloud sessions committed, without waiting for the next local write — and push
|
|
194
|
+
* anything a failed cycle left stranded locally, via the same owed-push rule as the writer.
|
|
195
|
+
* Ensures the checkout exists, so a fresh clone converges on its first tick. Never throws.
|
|
196
|
+
*
|
|
197
|
+
* Reports why it could not converge: a push origin rejects, or no origin to converge with at
|
|
198
|
+
* all. The writer treats a remote-less repo as fine — the commit is safe locally — but a sync's
|
|
199
|
+
* whole job is to meet the other machines, and a repo nothing can reach is an error state the
|
|
200
|
+
* caller has to surface, not a mode this supports.
|
|
201
|
+
*/
|
|
202
|
+
export async function pullFileBranch(repo, branch, deps = {}) {
|
|
203
|
+
const r = resolveDeps(deps);
|
|
204
|
+
const result = await withFileBranch(repo, branch, 'sync', async () => { }, deps);
|
|
205
|
+
const outcome = !result.ok
|
|
206
|
+
? { ok: false, error: result.error }
|
|
207
|
+
: (await hasRemote(repo, r.git))
|
|
208
|
+
? { ok: true }
|
|
209
|
+
: { ok: false, error: `the repository has no remote, so the ${branch} branch cannot be shared with other machines` };
|
|
210
|
+
if (!outcome.ok)
|
|
211
|
+
r.log(`[branches] ${branch}: ${outcome.error}`);
|
|
212
|
+
return outcome;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* The repository `cwd` belongs to: the directory holding the repo's real `.git`. From the main
|
|
216
|
+
* checkout that is `cwd` itself; from an agent's worktree it is the repo the worktree was made
|
|
217
|
+
* from — where the persistent checkout lives, and the address every funneled write goes to.
|
|
218
|
+
* `undefined` outside any repo.
|
|
219
|
+
*/
|
|
220
|
+
export async function fileBranchRepo(cwd, git = nodeGitRunner()) {
|
|
221
|
+
try {
|
|
222
|
+
const gitDir = (await git(['rev-parse', '--path-format=absolute', '--git-common-dir'], cwd)).trim();
|
|
223
|
+
return gitDir ? dirname(gitDir) : undefined;
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
return undefined;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* The refs a read looks at, in order. `fresh` fetches first and prefers origin's copy — for a
|
|
231
|
+
* reader about to act on the files from a long-lived process, where the local ref may trail what
|
|
232
|
+
* other writers pushed. Otherwise the local branch first, then origin's copy (a fresh clone that
|
|
233
|
+
* fetched but never branched — the cloud case).
|
|
234
|
+
*/
|
|
235
|
+
async function readRefs(repo, branch, fresh, git) {
|
|
236
|
+
const remote = fresh && (await hasRemote(repo, git));
|
|
237
|
+
if (remote)
|
|
238
|
+
await git(['fetch', 'origin', branch], repo).catch(() => { });
|
|
239
|
+
return remote ? [`origin/${branch}`, branch] : [branch, `origin/${branch}`];
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Read one file off the branch, from anywhere in the repo: the persistent checkout when this
|
|
243
|
+
* `cwd`'s repo has one, else `git show` against the local branch (worktrees share the repo's
|
|
244
|
+
* refs, so an agent's checkout reads the same files without holding any of them), else against
|
|
245
|
+
* `origin/…`. `undefined` when the file exists in none of them. Never throws.
|
|
246
|
+
*/
|
|
247
|
+
export async function readBranchFile(cwd, branch, rel, opts = {}, deps = {}) {
|
|
248
|
+
const r = resolveDeps(deps);
|
|
249
|
+
const repo = (await fileBranchRepo(cwd, r.git)) ?? cwd;
|
|
250
|
+
if (!opts.fresh) {
|
|
251
|
+
const path = fileBranchPath(repo, branch);
|
|
252
|
+
const fromCheckout = await r.git(['rev-parse', '--abbrev-ref', 'HEAD'], path).then(out => out.trim() === branch, () => false);
|
|
253
|
+
if (fromCheckout) {
|
|
254
|
+
const { readFile } = await import('node:fs/promises');
|
|
255
|
+
const md = await readFile(join(path, rel), 'utf8').catch(() => undefined);
|
|
256
|
+
if (md !== undefined)
|
|
257
|
+
return md;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
for (const ref of await readRefs(repo, branch, opts.fresh, r.git)) {
|
|
261
|
+
const md = await r.git(['show', `${ref}:${rel}`], cwd).catch(() => undefined);
|
|
262
|
+
if (md !== undefined)
|
|
263
|
+
return md;
|
|
264
|
+
}
|
|
265
|
+
return undefined;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* The entries of one directory on the branch, by name — a read off the ref, like
|
|
269
|
+
* {@link readBranchFile}, for a reader that holds no checkout. `[]` for a directory the branch
|
|
270
|
+
* does not have. Never throws.
|
|
271
|
+
*/
|
|
272
|
+
export async function listBranchDir(cwd, branch, dir, opts = {}, deps = {}) {
|
|
273
|
+
const r = resolveDeps(deps);
|
|
274
|
+
const repo = (await fileBranchRepo(cwd, r.git)) ?? cwd;
|
|
275
|
+
for (const ref of await readRefs(repo, branch, opts.fresh, r.git)) {
|
|
276
|
+
const out = await r.git(['ls-tree', '--name-only', `${ref}:${dir}`], cwd).catch(() => undefined);
|
|
277
|
+
if (out !== undefined)
|
|
278
|
+
return out.split('\n').map(line => line.trim()).filter(Boolean);
|
|
279
|
+
}
|
|
280
|
+
return [];
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Open the branch for reading from any clone, holding no checkout: fetch origin's copy once and
|
|
284
|
+
* read that (a one-shot reader — a command an agent runs — must see what other writers pushed,
|
|
285
|
+
* its own detached write included, which the local ref never moves for), else the local branch
|
|
286
|
+
* when there is no remote. Every read then goes to that one ref; nothing is fetched again.
|
|
287
|
+
*/
|
|
288
|
+
export async function openBranchReader(cwd, branch, deps = {}) {
|
|
289
|
+
const r = resolveDeps(deps);
|
|
290
|
+
const repo = (await fileBranchRepo(cwd, r.git)) ?? cwd;
|
|
291
|
+
const [ref] = await readRefs(repo, branch, true, r.git);
|
|
292
|
+
const at = (await refExists(repo, `refs/remotes/origin/${branch}`, r.git)) ? ref : branch;
|
|
293
|
+
return {
|
|
294
|
+
ref: at,
|
|
295
|
+
read: rel => r.git(['show', `${at}:${rel}`], cwd).catch(() => undefined),
|
|
296
|
+
list: dir => r
|
|
297
|
+
.git(['ls-tree', '--name-only', `${at}:${dir}`], cwd)
|
|
298
|
+
.then(out => out.split('\n').map(line => line.trim()).filter(Boolean))
|
|
299
|
+
.catch(() => []),
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Apply one change to the branch as a one-shot remote writer, from any clone: fetch origin's
|
|
304
|
+
* tip, check it out in a throwaway worktree, run `op` against it, commit, push straight to the
|
|
305
|
+
* branch, remove the worktree. A push that loses a race re-fetches, resets to origin's tip and
|
|
306
|
+
* re-runs the op, twice, the same intent rule as the funnel; a push that still fails throws with
|
|
307
|
+
* git's reason.
|
|
308
|
+
*
|
|
309
|
+
* Never the persistent checkout: that is a long-lived process's, whose funnel sweeps its
|
|
310
|
+
* checkout with `git add -A` and resets it on failure, so a second writer inside it would be
|
|
311
|
+
* committed under the wrong message or wiped. The local branch ref is not moved either — the
|
|
312
|
+
* persistent checkout, when there is one, converges on its own next pull.
|
|
313
|
+
*
|
|
314
|
+
* A branch origin does not have yet is born by the write itself, parentless. A repo with no
|
|
315
|
+
* remote refuses: a change nothing can reach is the caller's error state, not a mode.
|
|
316
|
+
*/
|
|
317
|
+
export async function writeFileBranchDetached(cwd, branch, message, op, deps = {}) {
|
|
318
|
+
const r = resolveDeps(deps);
|
|
319
|
+
if (!(await hasRemote(cwd, r.git)))
|
|
320
|
+
return { ok: false, reason: 'no-remote' };
|
|
321
|
+
const { mkdtemp, rm } = await import('node:fs/promises');
|
|
322
|
+
const { tmpdir } = await import('node:os');
|
|
323
|
+
const dir = await mkdtemp(join(tmpdir(), `${branch}-write-`));
|
|
324
|
+
// The remote's tip, or a parentless start for a branch origin does not have yet.
|
|
325
|
+
const tip = async () => {
|
|
326
|
+
await r.git(['fetch', 'origin', branch], cwd).catch(() => { });
|
|
327
|
+
if (await refExists(cwd, `refs/remotes/origin/${branch}`, r.git))
|
|
328
|
+
return `origin/${branch}`;
|
|
329
|
+
return (await r.git(['commit-tree', EMPTY_TREE, '-m', `create the ${branch} branch`], cwd)).trim();
|
|
330
|
+
};
|
|
331
|
+
try {
|
|
332
|
+
await r.git(['worktree', 'add', '--detach', dir, await tip()], cwd);
|
|
333
|
+
for (let attempt = 0;; attempt++) {
|
|
334
|
+
await op(dir);
|
|
335
|
+
await r.git(['add', '-A'], dir);
|
|
336
|
+
const staged = (await r.git(['status', '--porcelain'], dir)).trim();
|
|
337
|
+
if (!staged)
|
|
338
|
+
return { ok: true, changed: false };
|
|
339
|
+
await r.git(['commit', '-m', resolveMessage(message)], dir);
|
|
340
|
+
try {
|
|
341
|
+
await r.git(['push', 'origin', `HEAD:refs/heads/${branch}`], dir);
|
|
342
|
+
return { ok: true, changed: true };
|
|
343
|
+
}
|
|
344
|
+
catch (err) {
|
|
345
|
+
if (attempt >= 1)
|
|
346
|
+
throw err;
|
|
347
|
+
await r.git(['reset', '--hard', await tip()], dir);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
finally {
|
|
352
|
+
await r.git(['worktree', 'remove', '--force', dir], cwd).catch(() => { });
|
|
353
|
+
await r.git(['worktree', 'prune'], cwd).catch(() => { });
|
|
354
|
+
await rm(dir, { recursive: true, force: true }).catch(() => { });
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
/** {@link BranchFileFs} over `node:fs/promises`. */
|
|
358
|
+
export function nodeBranchFileFs() {
|
|
359
|
+
const fs = () => import('node:fs/promises');
|
|
360
|
+
return {
|
|
361
|
+
read: path => fs().then(f => f.readFile(path, 'utf8')),
|
|
362
|
+
write: async (path, content) => {
|
|
363
|
+
const f = await fs();
|
|
364
|
+
await f.mkdir(dirname(path), { recursive: true });
|
|
365
|
+
await f.writeFile(path, content, 'utf8');
|
|
366
|
+
},
|
|
367
|
+
remove: path => fs().then(f => f.rm(path)),
|
|
368
|
+
list: dir => fs().then(f => f.readdir(dir)).catch(() => []),
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
//# sourceMappingURL=file-branch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-branch.js","sourceRoot":"","sources":["../src/file-branch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type GitRunner } from './git.js';
|
|
2
|
+
/** The filesystem the exclude write needs; `node:fs/promises` in production. */
|
|
3
|
+
export interface ExcludeFs {
|
|
4
|
+
read(path: string): Promise<string>;
|
|
5
|
+
/** Recursive mkdir. */
|
|
6
|
+
mkdir(path: string): Promise<void>;
|
|
7
|
+
append(path: string, contents: string): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Append one ignore rule to the repository's `info/exclude` — the ignore file that is git's, not
|
|
11
|
+
* the project's, so no tracked file changes and no user ever sees a diff. The rule goes in the
|
|
12
|
+
* *common* git dir because git resolves excludes from there; a per-worktree copy looks right and
|
|
13
|
+
* is silently never read. One rule there covers every worktree of the repo. Idempotent. Throws on
|
|
14
|
+
* a non-repo or unwritable git dir — callers decide whether that is fatal (so far, never).
|
|
15
|
+
*/
|
|
16
|
+
export declare function excludeFromGit(repo: string, rule: string, fs?: ExcludeFs, git?: GitRunner): Promise<void>;
|
|
17
|
+
//# sourceMappingURL=git-exclude.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-exclude.d.ts","sourceRoot":"","sources":["../src/git-exclude.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,UAAU,CAAA;AAExD,gFAAgF;AAChF,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IACnC,uBAAuB;IACvB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACtD;AAWD;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,EAAE,GAAE,SAA2B,EAC/B,GAAG,GAAE,SAA2B,GAC/B,OAAO,CAAC,IAAI,CAAC,CASf"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { join, isAbsolute } from 'node:path';
|
|
2
|
+
import { nodeGitRunner } from './git.js';
|
|
3
|
+
function nodeExcludeFs() {
|
|
4
|
+
const fs = () => import('node:fs/promises');
|
|
5
|
+
return {
|
|
6
|
+
read: path => fs().then(f => f.readFile(path, 'utf8')),
|
|
7
|
+
mkdir: path => fs().then(f => f.mkdir(path, { recursive: true })).then(() => { }),
|
|
8
|
+
append: (path, contents) => fs().then(f => f.appendFile(path, contents)),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Append one ignore rule to the repository's `info/exclude` — the ignore file that is git's, not
|
|
13
|
+
* the project's, so no tracked file changes and no user ever sees a diff. The rule goes in the
|
|
14
|
+
* *common* git dir because git resolves excludes from there; a per-worktree copy looks right and
|
|
15
|
+
* is silently never read. One rule there covers every worktree of the repo. Idempotent. Throws on
|
|
16
|
+
* a non-repo or unwritable git dir — callers decide whether that is fatal (so far, never).
|
|
17
|
+
*/
|
|
18
|
+
export async function excludeFromGit(repo, rule, fs = nodeExcludeFs(), git = nodeGitRunner()) {
|
|
19
|
+
const common = (await git(['rev-parse', '--git-common-dir'], repo)).trim();
|
|
20
|
+
if (!common)
|
|
21
|
+
return;
|
|
22
|
+
const infoDir = join(isAbsolute(common) ? common : join(repo, common), 'info');
|
|
23
|
+
const path = join(infoDir, 'exclude');
|
|
24
|
+
const current = await fs.read(path).catch(() => '');
|
|
25
|
+
if (current.split('\n').some(line => line.trim() === rule))
|
|
26
|
+
return;
|
|
27
|
+
await fs.mkdir(infoDir);
|
|
28
|
+
await fs.append(path, (current && !current.endsWith('\n') ? '\n' : '') + rule + '\n');
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=git-exclude.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-exclude.js","sourceRoot":"","sources":["../src/git-exclude.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAkB,MAAM,UAAU,CAAA;AAUxD,SAAS,aAAa;IACpB,MAAM,EAAE,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAA;IAC3C,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACtD,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC;QAChF,MAAM,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;KACzE,CAAA;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,IAAY,EACZ,IAAY,EACZ,EAAE,GAAc,aAAa,EAAE,EAC/B,GAAG,GAAc,aAAa,EAAE;IAEhC,MAAM,MAAM,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,WAAW,EAAE,kBAAkB,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IAC1E,IAAI,CAAC,MAAM;QAAE,OAAM;IACnB,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAA;IAC9E,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;IACrC,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;IACnD,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,IAAI,CAAC;QAAE,OAAM;IAClE,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IACvB,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,CAAA;AACvF,CAAC"}
|
package/dist/git.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Running git: one `execFile`-backed runner with a per-subcommand timeout, and the two readings
|
|
3
|
+
* of a failed invocation every caller wants — was it a timeout kill, and which line is worth
|
|
4
|
+
* showing.
|
|
5
|
+
*/
|
|
6
|
+
/** Runs `git` in `cwd`, resolving its stdout. Rejects on a non-zero exit. */
|
|
7
|
+
export type GitRunner = (args: string[], cwd: string) => Promise<string>;
|
|
8
|
+
/**
|
|
9
|
+
* A local read: the index, a ref, or objects already on disk. Kept at the budget that used to
|
|
10
|
+
* cover everything, so a hung read still fails fast instead of holding the caller longer (#997).
|
|
11
|
+
*/
|
|
12
|
+
export declare const GIT_READ_TIMEOUT_MS = 10000;
|
|
13
|
+
/** A local mutation. Bounded by disk, but an index write on a large repo outlives a read. */
|
|
14
|
+
export declare const GIT_WRITE_TIMEOUT_MS = 30000;
|
|
15
|
+
/**
|
|
16
|
+
* The network, or a whole checkout. `git worktree add` writes every tracked file and `git push`
|
|
17
|
+
* uploads a packfile; on a large repo both routinely pass 10s, which is what #997 is about.
|
|
18
|
+
*/
|
|
19
|
+
export declare const GIT_SLOW_TIMEOUT_MS = 120000;
|
|
20
|
+
/**
|
|
21
|
+
* The timeout for one git invocation, chosen by subcommand (#997). One flat 10s budget once
|
|
22
|
+
* covered every call site, so the slowest two ran under what is really a read's budget: a
|
|
23
|
+
* SIGTERM'd `worktree add` drops an agent into the user's main checkout, a SIGTERM'd `push` may
|
|
24
|
+
* have half-landed.
|
|
25
|
+
*/
|
|
26
|
+
export declare function gitTimeoutMs(args: string[]): number;
|
|
27
|
+
/**
|
|
28
|
+
* A git killed for outrunning its timeout, as opposed to one git itself rejected (#997).
|
|
29
|
+
*
|
|
30
|
+
* `execFile` SIGTERMs on timeout, and a killed `git push` usually writes nothing to stderr, so
|
|
31
|
+
* without this the failure surfaces as a bare "Command failed: git push ..." that reads like a
|
|
32
|
+
* rejected push. The `timedOut` brand is what a caller across a package boundary checks.
|
|
33
|
+
*/
|
|
34
|
+
export declare class GitTimeoutError extends Error {
|
|
35
|
+
readonly args: string[];
|
|
36
|
+
readonly timeoutMs: number;
|
|
37
|
+
readonly timedOut = true;
|
|
38
|
+
constructor(args: string[], timeoutMs: number);
|
|
39
|
+
}
|
|
40
|
+
/** True when a {@link GitRunner} rejection is a timeout kill rather than a non-zero exit. */
|
|
41
|
+
export declare function isGitTimeout(err: unknown): err is GitTimeoutError;
|
|
42
|
+
/**
|
|
43
|
+
* A {@link GitRunner} backed by `execFile('git', ...)`. Rejects on any git error, and with a
|
|
44
|
+
* {@link GitTimeoutError} when the operation outran its {@link gitTimeoutMs} budget.
|
|
45
|
+
*
|
|
46
|
+
* The buffer is raised well past the default because a repo crawl (`git ls-files`) prints a
|
|
47
|
+
* line per file, and a large checkout overruns it.
|
|
48
|
+
*/
|
|
49
|
+
export declare function nodeGitRunner(): GitRunner;
|
|
50
|
+
/**
|
|
51
|
+
* Whether `cwd` sits inside a git working tree (#997). Lets a caller tell "this project cannot
|
|
52
|
+
* host a worktree at all" from "git was there and the operation failed", which are the same
|
|
53
|
+
* rejection out of `git worktree add` but call for opposite handling.
|
|
54
|
+
*
|
|
55
|
+
* Forgiving in one direction only: an unreadable / missing git reads as "no repo", which is the
|
|
56
|
+
* conservative answer for the caller that treats a repo's failure as fatal.
|
|
57
|
+
*/
|
|
58
|
+
export declare function isGitRepo(cwd: string, git?: GitRunner): Promise<boolean>;
|
|
59
|
+
/** The root of the checkout `cwd` is in — an agent's own, from anywhere under it. Rejects outside a repo. */
|
|
60
|
+
export declare function checkoutRoot(cwd: string, git?: GitRunner): Promise<string>;
|
|
61
|
+
/**
|
|
62
|
+
* The line of a failed git invocation worth showing: git's own `fatal:` / `error:` / `remote:`
|
|
63
|
+
* line when there is one, else the first line, else a placeholder.
|
|
64
|
+
*/
|
|
65
|
+
export declare function gitReason(err: unknown): string;
|
|
66
|
+
/** Push a branch to `origin`, setting its upstream. The failure is git's own line, not a stack. */
|
|
67
|
+
export declare function pushBranch(repo: string, branch: string, git?: GitRunner): Promise<{
|
|
68
|
+
ok: true;
|
|
69
|
+
} | {
|
|
70
|
+
ok: false;
|
|
71
|
+
error: string;
|
|
72
|
+
}>;
|
|
73
|
+
//# sourceMappingURL=git.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,6EAA6E;AAC7E,MAAM,MAAM,SAAS,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;AAExE;;;GAGG;AACH,eAAO,MAAM,mBAAmB,QAAS,CAAA;AAEzC,6FAA6F;AAC7F,eAAO,MAAM,oBAAoB,QAAS,CAAA;AAE1C;;;GAGG;AACH,eAAO,MAAM,mBAAmB,SAAU,CAAA;AA4C1C;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAgBnD;AAED;;;;;;GAMG;AACH,qBAAa,eAAgB,SAAQ,KAAK;IAGtC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM;IAH5B,QAAQ,CAAC,QAAQ,QAAO;IACxB,YACW,IAAI,EAAE,MAAM,EAAE,EACd,SAAS,EAAE,MAAM,EAI3B;CACF;AAED,6FAA6F;AAC7F,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,eAAe,CAEjE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,IAAI,SAAS,CAgBzC;AAED;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,SAA2B,GAAG,OAAO,CAAC,OAAO,CAAC,CAI/F;AAED,6GAA6G;AAC7G,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,SAA2B,GAAG,OAAO,CAAC,MAAM,CAAC,CAEjG;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAI9C;AAED,mGAAmG;AACnG,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,GAAG,GAAE,SAA2B,GAC/B,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAOtD"}
|