@biffo/cli 0.68.0 → 0.68.2
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/index.js +48 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -516,6 +516,26 @@ var GitAdapter = class {
|
|
|
516
516
|
const { stdout } = await execa("git", ["status", "--porcelain"], { cwd });
|
|
517
517
|
return stdout.trim().length > 0;
|
|
518
518
|
}
|
|
519
|
+
/** Best-effort fetch of the tracking remote, so the ahead/behind check below
|
|
520
|
+
* compares against reality rather than a stale local ref (#394). Never throws:
|
|
521
|
+
* offline or a missing remote must not block an upgrade on its own — the
|
|
522
|
+
* ahead/behind check that follows simply works from whatever is local. */
|
|
523
|
+
async fetch(cwd, remote = "origin") {
|
|
524
|
+
await execa("git", ["fetch", "--quiet", remote], { cwd, reject: false });
|
|
525
|
+
}
|
|
526
|
+
/** HEAD's position relative to its upstream. `hasUpstream` is false when the
|
|
527
|
+
* branch tracks nothing (or HEAD is detached), in which case currency cannot
|
|
528
|
+
* be established and ahead/behind are 0 (#394). */
|
|
529
|
+
async aheadBehind(cwd) {
|
|
530
|
+
const { stdout, exitCode } = await execa(
|
|
531
|
+
"git",
|
|
532
|
+
["rev-list", "--left-right", "--count", "HEAD...@{upstream}"],
|
|
533
|
+
{ cwd, reject: false }
|
|
534
|
+
);
|
|
535
|
+
if (exitCode !== 0) return { ahead: 0, behind: 0, hasUpstream: false };
|
|
536
|
+
const [ahead, behind] = stdout.trim().split(/\s+/).map((n) => Number.parseInt(n, 10));
|
|
537
|
+
return { ahead: ahead ?? 0, behind: behind ?? 0, hasUpstream: true };
|
|
538
|
+
}
|
|
519
539
|
/** The fetch URL of a remote (default "origin"). */
|
|
520
540
|
async getRemoteUrl(cwd, remote = "origin") {
|
|
521
541
|
const { stdout } = await execa("git", ["remote", "get-url", remote], { cwd });
|
|
@@ -2031,6 +2051,9 @@ var coreUpgradeCommand = new Command3("upgrade").description("Three-way-merge te
|
|
|
2031
2051
|
).option("--apply", "Apply the plan on a new branch and open a PR (default: dry run)").option("--allow-conflicts", "With --apply, open the PR even if some files conflict").option(
|
|
2032
2052
|
"--acknowledge-breaking",
|
|
2033
2053
|
"With --apply, proceed even though this upgrade crosses a documented breaking change"
|
|
2054
|
+
).option(
|
|
2055
|
+
"--allow-dirty",
|
|
2056
|
+
"Compute the plan even with uncommitted changes in the instance tree (they become part of the merge)"
|
|
2034
2057
|
).option("--base <branch>", "Base branch for the PR (defaults to the repo\u2019s default branch)").option("--remote <name>", "Git remote to push to and open the PR on (default: origin)").action(
|
|
2035
2058
|
async (options) => {
|
|
2036
2059
|
const cwd = options.cwd ? resolve3(options.cwd) : process.cwd();
|
|
@@ -2038,7 +2061,8 @@ var coreUpgradeCommand = new Command3("upgrade").description("Three-way-merge te
|
|
|
2038
2061
|
cwd,
|
|
2039
2062
|
apply: options.apply ?? false,
|
|
2040
2063
|
allowConflicts: options.allowConflicts ?? false,
|
|
2041
|
-
acknowledgeBreaking: options.acknowledgeBreaking ?? false
|
|
2064
|
+
acknowledgeBreaking: options.acknowledgeBreaking ?? false,
|
|
2065
|
+
allowDirty: options.allowDirty ?? false
|
|
2042
2066
|
};
|
|
2043
2067
|
if (options.templateRepo) runOptions.templateRepo = resolve3(options.templateRepo);
|
|
2044
2068
|
if (options.to) runOptions.toVersion = options.to;
|
|
@@ -2083,7 +2107,30 @@ async function runCoreUpgrade(options, deps = defaultDeps()) {
|
|
|
2083
2107
|
for (const c of cleanups) c();
|
|
2084
2108
|
}
|
|
2085
2109
|
}
|
|
2110
|
+
async function checkInstanceCurrency(git, cwd, allowDirty) {
|
|
2111
|
+
if (!await git.isGitRepo(cwd)) return;
|
|
2112
|
+
const branch = await git.currentBranch(cwd);
|
|
2113
|
+
if (branch === "HEAD" || branch === "") {
|
|
2114
|
+
throw new Error(
|
|
2115
|
+
`${cwd} has a detached HEAD, so the tree the plan would merge as "ours" has no branch identity. Check out the integration branch first (#394).`
|
|
2116
|
+
);
|
|
2117
|
+
}
|
|
2118
|
+
await git.fetch(cwd);
|
|
2119
|
+
const { ahead, behind, hasUpstream } = await git.aheadBehind(cwd);
|
|
2120
|
+
if (hasUpstream && behind > 0) {
|
|
2121
|
+
const diverged = ahead > 0 ? ` and ${ahead} ahead (diverged)` : "";
|
|
2122
|
+
throw new Error(
|
|
2123
|
+
`${cwd} is ${behind} commit(s) behind its upstream${diverged}. The plan would merge onto a stale tree and propose reintroducing superseded content. Run \`git pull\` first, or fix the divergence, then re-run (#394).`
|
|
2124
|
+
);
|
|
2125
|
+
}
|
|
2126
|
+
if (!allowDirty && await git.hasUncommittedChanges(cwd)) {
|
|
2127
|
+
throw new Error(
|
|
2128
|
+
`${cwd} has uncommitted changes, which would silently become part of the merge's "ours" side. Commit or stash them, or pass --allow-dirty to accept them deliberately (#394).`
|
|
2129
|
+
);
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2086
2132
|
async function runCoreUpgradeResolved(options, deps, cleanups) {
|
|
2133
|
+
await checkInstanceCurrency(deps.git, options.cwd, options.allowDirty ?? false);
|
|
2087
2134
|
const materialize = deps.materialize ?? materializeTemplateAtTag;
|
|
2088
2135
|
const templateRepo = options.templateRepo ? resolve3(options.templateRepo) : resolveTemplateRoot({ guidance: MISSING_TEMPLATE_ROOT_GUIDANCE2 });
|
|
2089
2136
|
const instanceVersion = readInstanceCoreVersion(options.cwd);
|
|
@@ -2181,9 +2228,6 @@ async function applyAndOpenPr(options, deps, plan, migrations, fromVersion, toVe
|
|
|
2181
2228
|
if (!await git.isGitRepo(options.cwd)) {
|
|
2182
2229
|
throw new Error(`${options.cwd} is not a git repository.`);
|
|
2183
2230
|
}
|
|
2184
|
-
if (await git.hasUncommittedChanges(options.cwd)) {
|
|
2185
|
-
throw new Error("The working tree has uncommitted changes. Commit or stash them first.");
|
|
2186
|
-
}
|
|
2187
2231
|
const branch = upgradeBranchName(fromVersion, toVersion);
|
|
2188
2232
|
log.step(1, 4, `Creating branch ${branch}`);
|
|
2189
2233
|
await git.createBranch(options.cwd, branch);
|