@bivy/bivy 0.10.1-staging.241 → 0.10.1-staging.243
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/dist/repo-workspace.js +37 -0
- package/dist/server.js +2 -1
- package/dist/session/fork-standup.js +4 -3
- package/dist/worktree.js +18 -8
- package/package.json +1 -1
package/dist/repo-workspace.js
CHANGED
|
@@ -215,6 +215,43 @@ export async function resolveAdoptBaseRef(repoDir, branch) {
|
|
|
215
215
|
return resolveDefaultBaseRef(repoDir);
|
|
216
216
|
}
|
|
217
217
|
}
|
|
218
|
+
/**
|
|
219
|
+
* Resolve the best base for a same-node fork without assuming the source branch
|
|
220
|
+
* ref still exists. Prefer the live source checkout because it preserves its
|
|
221
|
+
* exact committed tip, including commits that were never pushed. Then try the
|
|
222
|
+
* local and remote branch refs before degrading to the repository default.
|
|
223
|
+
*/
|
|
224
|
+
export async function resolveForkBaseRef(repoDir, branch, sourceWorktree) {
|
|
225
|
+
if (sourceWorktree) {
|
|
226
|
+
try {
|
|
227
|
+
const { stdout } = await exec("git", ["-C", sourceWorktree, "rev-parse", "--verify", "HEAD"], { cwd: sourceWorktree });
|
|
228
|
+
const head = stdout.trim();
|
|
229
|
+
if (head)
|
|
230
|
+
return head;
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
// The source checkout may have been pruned; continue through the refs.
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (branch) {
|
|
237
|
+
try {
|
|
238
|
+
await exec("git", ["-C", repoDir, "rev-parse", "--verify", "--quiet", `refs/heads/${branch}`], { cwd: repoDir });
|
|
239
|
+
return branch;
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
// The local branch may have been pruned or lost in a re-clone.
|
|
243
|
+
}
|
|
244
|
+
await fetchOrigin(repoDir);
|
|
245
|
+
try {
|
|
246
|
+
await exec("git", ["-C", repoDir, "rev-parse", "--verify", "--quiet", `refs/remotes/origin/${branch}`], { cwd: repoDir });
|
|
247
|
+
return `origin/${branch}`;
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
// A never-pushed branch is expected to be absent remotely.
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return resolveDefaultBaseRef(repoDir);
|
|
254
|
+
}
|
|
218
255
|
/**
|
|
219
256
|
* Whether an existing Bivy-owned checkout at `dest` can be reused as-is, i.e. it
|
|
220
257
|
* has a `.git` entry AND `git rev-parse` accepts it as a real repository. A
|
package/dist/server.js
CHANGED
|
@@ -76,7 +76,7 @@ import { checkDiskAdmission } from "./harness/disk-admission.js";
|
|
|
76
76
|
import { sandboxTier, setConfiguredSandboxTier, normalizeSandboxTier } from "./harness/sandbox.js";
|
|
77
77
|
import { setConfiguredAutoAttachToolImages } from "./harness/tool-image-attachments.js";
|
|
78
78
|
import { injectMcpProxyForSession, injectBivyToolsForSession } from "./harness/mcp-inject.js";
|
|
79
|
-
import { parseRepo, isGitHubSlugPart, inferGitHubRepoFromWorkspace, isSharedCloneRoot, resolveGitHubToken, ghCliInstalled, cloneOrUpdateRepo, resolveDefaultBaseRef, resolveBranchBaseRef, resolveAdoptBaseRef, fetchOrigin } from "./repo-workspace.js";
|
|
79
|
+
import { parseRepo, isGitHubSlugPart, inferGitHubRepoFromWorkspace, isSharedCloneRoot, resolveGitHubToken, ghCliInstalled, cloneOrUpdateRepo, resolveDefaultBaseRef, resolveBranchBaseRef, resolveAdoptBaseRef, resolveForkBaseRef, fetchOrigin } from "./repo-workspace.js";
|
|
80
80
|
import { configureGitAuth, writeGitCredentialEndpoint } from "./git-auth.js";
|
|
81
81
|
import { GitHubTaskPoller, resolveGitHubTaskConfig, buildTaskPrompt, buildResumePrompt, buildInteractiveResumePrompt, DEFAULT_ISSUE_INSTRUCTIONS, parseBivyDirectives, commitAll, pushBranch, mergeBaseIntoBranch, completeMerge, abortMerge, findOpenPullRequestForBranch, findPullRequestsForBranch, findMergedPullRequestForBranch, issueBranchName, getPullRequest, commentIssueOnce, listOpenLabelledIssues, selectActionableIssues, getIssue, getIssueCommentBody, addLabel, removeLabel, announcePickup, } from "./github-tasks.js";
|
|
82
82
|
import { buildLinearTaskPrompt, getLinearIssue, linearBranchName } from "./linear-tasks.js";
|
|
@@ -8045,6 +8045,7 @@ const forkStandUp = createForkStandUp({
|
|
|
8045
8045
|
createWorktree,
|
|
8046
8046
|
resolveDefaultBaseRef,
|
|
8047
8047
|
resolveAdoptBaseRef,
|
|
8048
|
+
resolveForkBaseRef,
|
|
8048
8049
|
applyDirtyPatch,
|
|
8049
8050
|
gitRepoRoot,
|
|
8050
8051
|
materializeFork,
|
|
@@ -77,10 +77,11 @@ export function createForkStandUp(deps) {
|
|
|
77
77
|
// pickups don't race on `git worktree add` or clobber each other's trees.
|
|
78
78
|
const wt = await deps.withRepoLock(repoDir, async () => {
|
|
79
79
|
if (opts.worktree === "fresh") {
|
|
80
|
-
// Same-node fork:
|
|
81
|
-
//
|
|
80
|
+
// Same-node fork: preserve the live source tip when possible, but do
|
|
81
|
+
// not fail when its checkout or branch ref has already been pruned.
|
|
82
82
|
const forkBranch = `${srcBranch ?? "fork"}-fork-${randomBytes(4).toString("hex")}`;
|
|
83
|
-
|
|
83
|
+
const base = await deps.resolveForkBaseRef(repoDir, srcBranch, bundle.record.worktree);
|
|
84
|
+
return deps.createWorktree({ repoDir, id: forkBranch, branch: forkBranch, base });
|
|
84
85
|
}
|
|
85
86
|
// Cross-node adopt: the source branch has no LOCAL ref here. Base the
|
|
86
87
|
// adopted branch on the pushed `origin/<branch>` so committed work travels
|
package/dist/worktree.js
CHANGED
|
@@ -77,16 +77,26 @@ export async function createWorktree(opts) {
|
|
|
77
77
|
// first, then check the existing branch out into a fresh worktree.
|
|
78
78
|
const localExists = await refExists(repoRoot, `refs/heads/${branch}`);
|
|
79
79
|
const remoteExists = !localExists && (await refExists(repoRoot, `refs/remotes/origin/${branch}`));
|
|
80
|
-
if (
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
80
|
+
if (localExists || remoteExists) {
|
|
81
|
+
await removeWorktree(repoRoot, wtPath);
|
|
82
|
+
fs.rmSync(wtPath, { recursive: true, force: true });
|
|
83
|
+
if (localExists) {
|
|
84
|
+
await exec("git", ["-C", repoRoot, "worktree", "add", wtPath, branch]);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
// Recreate the local branch from origin, then check it out in the worktree.
|
|
88
|
+
await exec("git", ["-C", repoRoot, "worktree", "add", "-b", branch, wtPath, `origin/${branch}`]);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
else if (base !== "HEAD" && !(await refExists(repoRoot, base))) {
|
|
92
|
+
// Defense in depth for stale internal metadata: callers should resolve a
|
|
93
|
+
// fork base first, but a missing ref must not prevent session stand-up.
|
|
94
|
+
await removeWorktree(repoRoot, wtPath);
|
|
95
|
+
fs.rmSync(wtPath, { recursive: true, force: true });
|
|
96
|
+
await exec("git", ["-C", repoRoot, "worktree", "add", "-b", branch, wtPath, "HEAD"]);
|
|
86
97
|
}
|
|
87
98
|
else {
|
|
88
|
-
|
|
89
|
-
await exec("git", ["-C", repoRoot, "worktree", "add", "-b", branch, wtPath, `origin/${branch}`]);
|
|
99
|
+
throw error;
|
|
90
100
|
}
|
|
91
101
|
}
|
|
92
102
|
// Opportunistically reuse a sibling worktree's installed deps (node_modules,
|
package/package.json
CHANGED