unirepo-cli 0.5.3 → 0.6.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/package.json +1 -1
- package/src/commands/reset.js +104 -0
- package/src/git.js +11 -0
- package/src/index.js +12 -0
- package/src/ui.js +8 -0
package/package.json
CHANGED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import {
|
|
2
|
+
git,
|
|
3
|
+
getMonorepoRoot,
|
|
4
|
+
getRemoteBranch,
|
|
5
|
+
getSubtreePrefixes,
|
|
6
|
+
setConfiguredSubtreeBranch,
|
|
7
|
+
unsetConfiguredSubtreePushBranch,
|
|
8
|
+
setLastPushedRef,
|
|
9
|
+
hasUncommittedChanges,
|
|
10
|
+
} from '../git.js';
|
|
11
|
+
import { validateGitSubtree, validateInsideMonorepo } from '../validate.js';
|
|
12
|
+
import * as ui from '../ui.js';
|
|
13
|
+
|
|
14
|
+
export async function runReset({ subtrees: requestedSubtrees, branch, dryRun }) {
|
|
15
|
+
const cwd = getMonorepoRoot(process.cwd());
|
|
16
|
+
|
|
17
|
+
ui.header('Resetting subtrees');
|
|
18
|
+
ui.blank();
|
|
19
|
+
|
|
20
|
+
validateGitSubtree();
|
|
21
|
+
validateInsideMonorepo(cwd);
|
|
22
|
+
|
|
23
|
+
if (hasUncommittedChanges(cwd)) {
|
|
24
|
+
ui.error('You have uncommitted changes. Commit or stash before resetting.');
|
|
25
|
+
ui.blank();
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const allPrefixes = getSubtreePrefixes(cwd);
|
|
30
|
+
|
|
31
|
+
let prefixes;
|
|
32
|
+
if (requestedSubtrees && requestedSubtrees.length > 0) {
|
|
33
|
+
const names = new Set(allPrefixes.map((p) => p.name));
|
|
34
|
+
for (const name of requestedSubtrees) {
|
|
35
|
+
if (!names.has(name)) {
|
|
36
|
+
throw new Error(`"${name}" is not a tracked subtree. Run "unirepo status" to see available subtrees.`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
prefixes = allPrefixes.filter((p) => requestedSubtrees.includes(p.name));
|
|
40
|
+
} else {
|
|
41
|
+
prefixes = allPrefixes;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (prefixes.length === 0) {
|
|
45
|
+
ui.info('No tracked subtrees found. Nothing to reset.');
|
|
46
|
+
ui.blank();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Use the remote's actual default branch — not the stored config which may be a stale feature branch.
|
|
51
|
+
const targets = prefixes.map((p) => ({
|
|
52
|
+
...p,
|
|
53
|
+
upstreamBranch: branch || getRemoteBranch(cwd, p.name) || 'main',
|
|
54
|
+
}));
|
|
55
|
+
|
|
56
|
+
const uniqueBranches = [...new Set(targets.map((t) => t.upstreamBranch))];
|
|
57
|
+
if (uniqueBranches.length === 1) {
|
|
58
|
+
ui.info(`Resetting all subtrees to branch: ${uniqueBranches[0]}`);
|
|
59
|
+
} else {
|
|
60
|
+
ui.info('Resetting subtrees to their upstream default branches');
|
|
61
|
+
}
|
|
62
|
+
ui.blank();
|
|
63
|
+
|
|
64
|
+
if (dryRun) {
|
|
65
|
+
const commands = targets.flatMap((t) => [
|
|
66
|
+
`git subtree pull --squash --prefix="${t.name}" "${t.name}" "${t.upstreamBranch}"`,
|
|
67
|
+
`git config unirepo.subtree.${t.name}.branch "${t.upstreamBranch}"`,
|
|
68
|
+
`git config --unset unirepo.subtree.${t.name}.pushBranch`,
|
|
69
|
+
]);
|
|
70
|
+
ui.dryRun(commands);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let succeeded = 0;
|
|
75
|
+
let failed = 0;
|
|
76
|
+
|
|
77
|
+
for (let i = 0; i < targets.length; i++) {
|
|
78
|
+
const target = targets[i];
|
|
79
|
+
ui.repoStep(i + 1, targets.length, target.name, 'Resetting');
|
|
80
|
+
ui.repoDetail('Branch', target.upstreamBranch);
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
git(`subtree pull --squash --prefix="${target.name}" "${target.name}" "${target.upstreamBranch}"`, { cwd });
|
|
84
|
+
const headRef = git('rev-parse HEAD', { cwd, silent: true });
|
|
85
|
+
setConfiguredSubtreeBranch(cwd, target.name, target.upstreamBranch);
|
|
86
|
+
unsetConfiguredSubtreePushBranch(cwd, target.name);
|
|
87
|
+
setLastPushedRef(cwd, target.name, headRef);
|
|
88
|
+
ui.success(`${target.name} reset to ${target.upstreamBranch}`);
|
|
89
|
+
succeeded++;
|
|
90
|
+
} catch (err) {
|
|
91
|
+
ui.error(`Failed to reset ${target.name}: ${err.message}`);
|
|
92
|
+
failed++;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
ui.blank();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (failed === 0) {
|
|
99
|
+
ui.success(`All ${succeeded} subtree(s) reset`);
|
|
100
|
+
} else {
|
|
101
|
+
ui.warning(`${succeeded} reset, ${failed} failed`);
|
|
102
|
+
}
|
|
103
|
+
ui.blank();
|
|
104
|
+
}
|
package/src/git.js
CHANGED
|
@@ -316,6 +316,17 @@ export function setConfiguredSubtreePushBranch(cwd, prefixName, branch) {
|
|
|
316
316
|
);
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
+
/**
|
|
320
|
+
* Remove the push/head branch override for a subtree from local git config.
|
|
321
|
+
*/
|
|
322
|
+
export function unsetConfiguredSubtreePushBranch(cwd, prefixName) {
|
|
323
|
+
git(`config --unset ${quoteShellArg(getSubtreePushBranchConfigKey(prefixName))}`, {
|
|
324
|
+
cwd,
|
|
325
|
+
silent: true,
|
|
326
|
+
allowFailure: true,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
|
|
319
330
|
/**
|
|
320
331
|
* Get the SHA of the last HEAD that was successfully pushed for a subtree.
|
|
321
332
|
*/
|
package/src/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { runStatus } from './commands/status.js';
|
|
|
9
9
|
import { runPush } from './commands/push.js';
|
|
10
10
|
import { runBranch } from './commands/branch.js';
|
|
11
11
|
import { runPr } from './commands/pr.js';
|
|
12
|
+
import { runReset } from './commands/reset.js';
|
|
12
13
|
import { pathToFileURL } from 'node:url';
|
|
13
14
|
import { realpathSync } from 'node:fs';
|
|
14
15
|
|
|
@@ -73,6 +74,7 @@ const COMMAND_USAGE = {
|
|
|
73
74
|
push: 'Usage: unirepo push [subtree...] [--branch <name>] [--dry-run]',
|
|
74
75
|
branch: 'Usage: unirepo branch [name]',
|
|
75
76
|
pr: 'Usage: unirepo pr [subtree...] --title <title> [--body <text>] [--base <name>] [--head <name>] [--draft] [--dry-run]',
|
|
77
|
+
reset: 'Usage: unirepo reset [subtree...] [--branch <name>] [--dry-run]',
|
|
76
78
|
};
|
|
77
79
|
|
|
78
80
|
const COMMAND_FLAGS = {
|
|
@@ -84,6 +86,7 @@ const COMMAND_FLAGS = {
|
|
|
84
86
|
push: new Set(['branch', 'dryRun']),
|
|
85
87
|
branch: new Set(),
|
|
86
88
|
pr: new Set(['title', 'body', 'base', 'head', 'draft', 'dryRun']),
|
|
89
|
+
reset: new Set(['branch', 'dryRun']),
|
|
87
90
|
};
|
|
88
91
|
|
|
89
92
|
const FLAG_NAMES = {
|
|
@@ -227,6 +230,15 @@ export async function main() {
|
|
|
227
230
|
break;
|
|
228
231
|
}
|
|
229
232
|
|
|
233
|
+
case 'reset': {
|
|
234
|
+
await runReset({
|
|
235
|
+
subtrees: positional.length > 0 ? positional : undefined,
|
|
236
|
+
branch: flags.branch,
|
|
237
|
+
dryRun: flags.dryRun || false,
|
|
238
|
+
});
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
|
|
230
242
|
default:
|
|
231
243
|
ui.error(`Unknown command: ${command}`);
|
|
232
244
|
ui.blank();
|
package/src/ui.js
CHANGED
|
@@ -181,6 +181,7 @@ ${chalk.bold('Commands:')}
|
|
|
181
181
|
${chalk.green('push')} [subtree...] Push changed subtrees upstream
|
|
182
182
|
${chalk.green('pr')} [subtree...] Open PRs for changed subtree repos
|
|
183
183
|
${chalk.green('branch')} [name] Create or show the workspace branch
|
|
184
|
+
${chalk.green('reset')} [subtree...] Pull from upstream main and align all push branches
|
|
184
185
|
${chalk.green('version')} Show the installed CLI version
|
|
185
186
|
|
|
186
187
|
${chalk.bold('Global options:')}
|
|
@@ -207,6 +208,10 @@ ${chalk.bold('Push options:')}
|
|
|
207
208
|
--branch <name> Branch name for all selected subtree pushes
|
|
208
209
|
--dry-run Show commands without executing
|
|
209
210
|
|
|
211
|
+
${chalk.bold('Reset options:')}
|
|
212
|
+
--branch <name> Branch to pull from and align push targets to (default: each subtree's upstream default)
|
|
213
|
+
--dry-run Show commands without executing
|
|
214
|
+
|
|
210
215
|
${chalk.bold('PR options:')}
|
|
211
216
|
--title <title> Shared PR title (required)
|
|
212
217
|
--body <text> Shared PR description
|
|
@@ -238,6 +243,9 @@ ${chalk.bold('Examples:')}
|
|
|
238
243
|
${chalk.dim('# Override one subtree push/PR branch via local git config')}
|
|
239
244
|
git config unirepo.subtree.api.pushBranch feature-api
|
|
240
245
|
|
|
246
|
+
${chalk.dim('# After merging a feature, reset all subtrees back to main')}
|
|
247
|
+
npx unirepo reset
|
|
248
|
+
|
|
241
249
|
${chalk.dim('# Push changes upstream')}
|
|
242
250
|
npx unirepo push --dry-run
|
|
243
251
|
|