polyrepo-cli 1.0.0 → 1.0.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/README.md +139 -38
- package/package.json +1 -1
- package/src/changes.js +14 -14
- package/src/commands/bump.js +9 -8
- package/src/commands/doctor.js +272 -6
- package/src/commands/list.js +1 -2
- package/src/commands/publish.js +41 -4
- package/src/commands/release.js +16 -3
- package/src/commands/switchMaster.js +24 -11
- package/src/commands/tag.js +22 -9
- package/src/config.js +11 -2
- package/src/github.js +1 -1
- package/src/index.js +92 -21
- package/src/masterSync.js +26 -9
- package/src/repos.js +44 -5
- package/test/config.test.js +22 -0
package/README.md
CHANGED
|
@@ -23,12 +23,16 @@ with `--dry-run` before anything actually changes.
|
|
|
23
23
|
- **`prs`** — one table of every open pull request across every
|
|
24
24
|
package (`gh pr list`) — useful after an interrupted `bump` run to
|
|
25
25
|
see what's still waiting to be merged.
|
|
26
|
-
- **`doctor`** — a one-command health check:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
`
|
|
26
|
+
- **`doctor`** — a one-command health check: environment (Node/git/gh/npm,
|
|
27
|
+
authentication), branch health (default-branch name drift, stale
|
|
28
|
+
remote-tracking refs, divergence from origin, detached `HEAD`,
|
|
29
|
+
missing branch protection, leftover `bump` branches — with a few
|
|
30
|
+
safe, non-destructive self-repairs along the way, and
|
|
31
|
+
`--clean-branches` for an interactive local-branch cleanup), and
|
|
32
|
+
cross-package dependency drift.
|
|
33
|
+
- **`switch-master`** — fast-forward selected repos to their up-to-date
|
|
34
|
+
default branch, whatever it's actually named (`master`, `main`, or
|
|
35
|
+
anything else — detected per repo, not assumed).
|
|
32
36
|
- **`bump`** — bump a package's version (patch by default, or
|
|
33
37
|
`--minor`/`--major`) through a branch → PR → merge, then tag the
|
|
34
38
|
release. Safe to re-run if a previous attempt was interrupted
|
|
@@ -265,21 +269,77 @@ polyrepo prs
|
|
|
265
269
|
polyrepo prs --packages vue-toast-kit,os-detect
|
|
266
270
|
```
|
|
267
271
|
|
|
268
|
-
### `polyrepo doctor`
|
|
272
|
+
### `polyrepo doctor [options]`
|
|
269
273
|
|
|
270
|
-
A
|
|
274
|
+
A health check in seven sections — mostly read-only diagnosis, plus a
|
|
275
|
+
few small, non-destructive self-repairs:
|
|
271
276
|
|
|
272
277
|
1. **Environment** — Node.js version (20+ required), whether
|
|
273
278
|
`git`/`gh`/`npm` are on `PATH`, and whether `gh`/`npm` are
|
|
274
279
|
authenticated (npm auth is only a warning — it's only needed for
|
|
275
280
|
`publish`).
|
|
276
281
|
2. **Config** — how many packages the current config actually
|
|
277
|
-
resolves to, and which repos are dirty
|
|
278
|
-
|
|
282
|
+
resolves to, and which repos are dirty, in a detached `HEAD` state,
|
|
283
|
+
or off their default branch.
|
|
284
|
+
3. **Remote sync** — two related repairs, both per-repo pointer
|
|
285
|
+
refreshes that never touch a file, branch, or commit:
|
|
286
|
+
- compares each repo's locally cached default-branch name (the
|
|
287
|
+
same value `switch-master`/`bump`/`tag` all use — see
|
|
288
|
+
`switch-master` above) against what GitHub actually reports
|
|
289
|
+
right now. Git never refreshes that local cache on its own, so
|
|
290
|
+
renaming a repo's default branch on GitHub after it was cloned
|
|
291
|
+
would otherwise go unnoticed by every other command forever —
|
|
292
|
+
wherever it's drifted, this fixes it with `git remote set-head
|
|
293
|
+
origin --auto`;
|
|
294
|
+
- runs `git remote prune origin` on every repo, dropping local
|
|
295
|
+
`remotes/origin/x` refs left over for branches already deleted
|
|
296
|
+
on GitHub (these tend to accumulate — every PR branch a `bump`
|
|
297
|
+
or manual workflow ever created and later got deleted on GitHub
|
|
298
|
+
leaves one behind locally until pruned).
|
|
299
|
+
|
|
300
|
+
Both are skipped for a repo GitHub can't be reached for (offline,
|
|
301
|
+
or `gh` not authenticated).
|
|
302
|
+
4. **Branch sync** — fetches and compares each repo's local default
|
|
303
|
+
branch against `origin/<default>`: **diverged** (both ahead and
|
|
304
|
+
behind — a fast-forward won't work, needs resolving by hand),
|
|
305
|
+
**behind only** (safe to fast-forward with `switch-master`), or
|
|
306
|
+
**ahead only** (local commits not yet pushed). Surfaces this
|
|
307
|
+
before some other command trips over it mid-run instead of after.
|
|
308
|
+
5. **Branch protection** — whether each repo's default branch
|
|
309
|
+
actually has GitHub branch protection enabled right now.
|
|
310
|
+
Report-only; enabling protection is a policy decision, not
|
|
311
|
+
something this fixes on your behalf.
|
|
312
|
+
6. **Stale bump branches** — `bump` merges through a PR with the
|
|
313
|
+
branch intentionally left on origin (`--delete-branch=false`, see
|
|
314
|
+
`bump` above), so every completed bump leaves a local branch copy
|
|
315
|
+
behind too, forever. This reports how many local branches match
|
|
316
|
+
`<version>-version-bump` **and** already have a merged PR.
|
|
317
|
+
`--clean-branches` turns that into a checkbox — pick which ones to
|
|
318
|
+
delete locally (`git branch -d`, which refuses instead of forcing
|
|
319
|
+
if a branch somehow isn't actually fully merged locally; the
|
|
320
|
+
branch on origin is never touched, deleting that is out of scope
|
|
321
|
+
here — it's more sensitive shared state).
|
|
322
|
+
7. **Cross-package dependencies** — the same dependency-drift check
|
|
279
323
|
that runs at the end of `bump`, available on demand without
|
|
280
324
|
bumping anything.
|
|
281
325
|
|
|
282
|
-
Worth running first if any other command is behaving unexpectedly
|
|
326
|
+
Worth running first if any other command is behaving unexpectedly, any
|
|
327
|
+
time you rename a default branch on GitHub, or just periodically to
|
|
328
|
+
catch accumulated cruft (stale remote-tracking refs, leftover bump
|
|
329
|
+
branches) before it piles up.
|
|
330
|
+
|
|
331
|
+
**Options:**
|
|
332
|
+
|
|
333
|
+
| Flag | What it does |
|
|
334
|
+
| --- | --- |
|
|
335
|
+
| `--clean-branches` | After scanning, show a checkbox of local bump branches whose PR is already merged; delete the ones you pick. |
|
|
336
|
+
|
|
337
|
+
```bash
|
|
338
|
+
polyrepo doctor
|
|
339
|
+
|
|
340
|
+
# also review and clean up leftover local bump branches
|
|
341
|
+
polyrepo doctor --clean-branches
|
|
342
|
+
```
|
|
283
343
|
|
|
284
344
|
```bash
|
|
285
345
|
polyrepo doctor
|
|
@@ -287,22 +347,55 @@ polyrepo doctor
|
|
|
287
347
|
|
|
288
348
|
### `polyrepo switch-master` (alias `sm`)
|
|
289
349
|
|
|
290
|
-
|
|
291
|
-
|
|
350
|
+
Each repo's **default branch is detected per repo**, not assumed —
|
|
351
|
+
GitHub itself defaults a new repo to `main`, and plenty of people
|
|
352
|
+
rename it (`master` included), so a mixed folder of repos can easily
|
|
353
|
+
have some on `master` and some on `main`. Detection prefers the
|
|
354
|
+
locally cached `origin/HEAD` ref (no network — set by `git clone`),
|
|
355
|
+
falls back to asking origin directly (`git ls-remote --symref`,
|
|
356
|
+
read-only), then to whichever of `master`/`main` exists as a local
|
|
357
|
+
branch, and finally to `main` (GitHub's own default) if nothing else
|
|
358
|
+
could tell it.
|
|
359
|
+
|
|
360
|
+
1. Shows a checkbox list of every repo with its current branch (and
|
|
361
|
+
its default branch, when the two differ); repos not currently on
|
|
362
|
+
their default branch are pre-selected.
|
|
292
363
|
2. After confirming, for each selected repo, one at a time (each
|
|
293
364
|
step's result prints immediately, not after the whole batch):
|
|
294
365
|
- a dirty working tree is skipped with a warning, untouched;
|
|
295
|
-
- otherwise: `git fetch origin` → `git checkout
|
|
296
|
-
`git merge --ff-only origin
|
|
297
|
-
3. If local
|
|
366
|
+
- otherwise: `git fetch origin` → `git checkout <default branch>` →
|
|
367
|
+
`git merge --ff-only origin/<default branch>`.
|
|
368
|
+
3. If the local default branch has diverged from origin (fast-forward
|
|
298
369
|
isn't possible), that repo is reported and left alone to resolve by
|
|
299
|
-
hand
|
|
370
|
+
hand.
|
|
371
|
+
|
|
372
|
+
`--force` changes step 2 and 3 for every selected repo: a dirty
|
|
373
|
+
working tree is no longer skipped, and each repo gets
|
|
374
|
+
`git checkout -f <default branch>` + `git reset --hard
|
|
375
|
+
origin/<default branch>` instead of the safe fast-forward-only merge —
|
|
376
|
+
uncommitted changes to tracked files and any local-only commits on
|
|
377
|
+
that branch are permanently discarded (untracked files are left
|
|
378
|
+
alone, this isn't `git clean`). The checkbox marks which selected
|
|
379
|
+
repos would lose changes, and the proceed confirmation says how many
|
|
380
|
+
and defaults to "No" instead of "Yes" whenever `--force` would
|
|
381
|
+
actually discard something.
|
|
382
|
+
|
|
383
|
+
**Options:**
|
|
384
|
+
|
|
385
|
+
| Flag | What it does |
|
|
386
|
+
| --- | --- |
|
|
387
|
+
| `--packages <a,b,c>` | Package list instead of the interactive checkbox. |
|
|
388
|
+
| `--yes` | Skip the "proceed?" confirmation. |
|
|
389
|
+
| `--force` | Discard uncommitted changes and local-only commits on the default branch, hard-resetting it to origin. |
|
|
300
390
|
|
|
301
391
|
```bash
|
|
302
392
|
polyrepo switch-master
|
|
303
393
|
|
|
304
394
|
# no checkbox, specific repos, no confirmation — for scripts
|
|
305
395
|
polyrepo switch-master --packages vue-toast-kit,os-detect --yes
|
|
396
|
+
|
|
397
|
+
# discard local changes on a repo you don't need anymore
|
|
398
|
+
polyrepo switch-master --packages vue-toast-kit --force
|
|
306
399
|
```
|
|
307
400
|
|
|
308
401
|
### `polyrepo bump [options]`
|
|
@@ -313,15 +406,17 @@ polyrepo switch-master --packages vue-toast-kit,os-detect --yes
|
|
|
313
406
|
the parts below it to `0`, same as any semver tool). Packages
|
|
314
407
|
with a dirty working tree are marked — they'll be skipped. The
|
|
315
408
|
highlighted package's description shows what's actually changed
|
|
316
|
-
since the last git tag (`git log <tag
|
|
317
|
-
there's probably nothing worth bumping. These
|
|
318
|
-
for every package in parallel, not one at a
|
|
409
|
+
since the last git tag (`git log <tag>..<default branch>`) — if
|
|
410
|
+
that's empty, there's probably nothing worth bumping. These
|
|
411
|
+
previews are computed for every package in parallel, not one at a
|
|
412
|
+
time.
|
|
319
413
|
2. After confirming, for each selected package, one at a time, with
|
|
320
414
|
live progress:
|
|
321
|
-
1. `git fetch origin` → `git checkout
|
|
322
|
-
`git merge --ff-only origin
|
|
323
|
-
created from an up-to-date
|
|
324
|
-
|
|
415
|
+
1. `git fetch origin` → `git checkout <default branch>` →
|
|
416
|
+
`git merge --ff-only origin/<default branch>` (the bump branch
|
|
417
|
+
is always created from an up-to-date default branch — detected
|
|
418
|
+
per repo, see `switch-master` above — not whatever branch the
|
|
419
|
+
repo happened to be on);
|
|
325
420
|
2. **checks the state of a previous attempt** — is there already a
|
|
326
421
|
merged PR, an open PR, or just a pushed branch named
|
|
327
422
|
`<new-version>-version-bump` (e.g. `1.2.10-version-bump` — the
|
|
@@ -329,8 +424,8 @@ polyrepo switch-master --packages vue-toast-kit,os-detect --yes
|
|
|
329
424
|
different bumps never collide). Depending on what's found, it
|
|
330
425
|
resumes from the right place instead of failing on "branch
|
|
331
426
|
already exists" or opening a duplicate PR:
|
|
332
|
-
- **already merged** — nothing to do (
|
|
333
|
-
in step 1), go straight to tagging;
|
|
427
|
+
- **already merged** — nothing to do (the default branch was
|
|
428
|
+
already synced in step 1), go straight to tagging;
|
|
334
429
|
- **open PR exists** — merge that one, don't open a new one;
|
|
335
430
|
- **branch pushed, no PR** — reuse the branch, open a PR;
|
|
336
431
|
- **nothing exists** — the full flow from scratch.
|
|
@@ -344,7 +439,8 @@ polyrepo switch-master --packages vue-toast-kit,os-detect --yes
|
|
|
344
439
|
review, not a finished changelog. Packages without a
|
|
345
440
|
`CHANGELOG.md` don't get one created. Both files are committed
|
|
346
441
|
together;
|
|
347
|
-
4. `gh pr create` against
|
|
442
|
+
4. `gh pr create` against the default branch (if there isn't one
|
|
443
|
+
already);
|
|
348
444
|
5. with `--wait-checks`: wait for the PR's CI checks via
|
|
349
445
|
`gh pr checks --watch` (with a real terminal, live-updating). No
|
|
350
446
|
checks configured isn't an error — there's just nothing to wait
|
|
@@ -352,9 +448,9 @@ polyrepo switch-master --packages vue-toast-kit,os-detect --yes
|
|
|
352
448
|
skip the merge;
|
|
353
449
|
6. `gh pr merge --merge` — through a PR, not a direct push, since
|
|
354
450
|
these repos require it;
|
|
355
|
-
7. `git checkout
|
|
356
|
-
`git merge --ff-only origin
|
|
357
|
-
the just-merged PR;
|
|
451
|
+
7. `git checkout <default branch>` → `git fetch origin` →
|
|
452
|
+
`git merge --ff-only origin/<default branch>` — local default
|
|
453
|
+
branch is synced to the just-merged PR;
|
|
358
454
|
8. **git tag** `v<new-version>` (e.g. `v1.2.10`) is created and
|
|
359
455
|
pushed if it doesn't already exist (idempotent, like everything
|
|
360
456
|
else here — a re-run won't try to create it twice).
|
|
@@ -414,11 +510,16 @@ polyrepo bump --packages vue-toast-kit,os-detect --yes
|
|
|
414
510
|
they differ (genuinely unpublished) are pre-selected; already
|
|
415
511
|
published ones are unchecked but still selectable (e.g. to
|
|
416
512
|
republish after an unpublish).
|
|
417
|
-
3. After confirming,
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
513
|
+
3. After confirming, checks `npm whoami` once for the whole batch
|
|
514
|
+
(skipped under `--dry-run`) — not logged in runs `npm login`
|
|
515
|
+
(a real terminal, so its browser-based OTP flow or credential
|
|
516
|
+
prompt works normally) before publishing anything, instead of
|
|
517
|
+
only finding out partway through the first package's `npm publish`.
|
|
518
|
+
4. For each selected package, one at a time: `npm publish` (or
|
|
519
|
+
`npm publish --dry-run` with the `--dry-run` flag — npm's own dry
|
|
520
|
+
run, including the real build and pack step, not just printing a
|
|
521
|
+
plan). Runs with a real terminal, not captured — an npm 2FA/OTP
|
|
522
|
+
prompt works normally.
|
|
422
523
|
|
|
423
524
|
**Options:**
|
|
424
525
|
|
|
@@ -453,9 +554,9 @@ it again or opening a PR:
|
|
|
453
554
|
pre-selected; already-tagged ones can still be picked manually
|
|
454
555
|
(harmless — it just confirms the tag is there).
|
|
455
556
|
3. After confirming, for each selected package, one at a time:
|
|
456
|
-
`git fetch`/`checkout
|
|
457
|
-
|
|
458
|
-
missing.
|
|
557
|
+
`git fetch`/`checkout <default branch>`/`merge --ff-only` (tags an
|
|
558
|
+
up-to-date default branch, same as `bump`), then creates and pushes
|
|
559
|
+
the tag if it's missing.
|
|
459
560
|
4. If at least one package was actually tagged (and it wasn't a
|
|
460
561
|
`--dry-run`), it asks: "Create a GitHub Release for the N
|
|
461
562
|
package(s) just tagged?" — answering yes runs the same process as
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polyrepo-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Interactive CLI for managing a folder of local npm package repos: version bumps through a PR, npm publish, GitHub releases, and cross-package dependency drift checks — all from one tool.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/changes.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { MASTER_BRANCH } from './config.js'
|
|
2
1
|
import { git, gitAsync } from './exec.js'
|
|
3
2
|
import { pMap } from './pMap.js'
|
|
4
3
|
|
|
5
4
|
const PREVIEW_LIMIT = 5
|
|
6
5
|
|
|
7
|
-
// Summarizes what changed on local
|
|
8
|
-
// last few commits if there's no tag yet), so the bump
|
|
9
|
-
// whether a package actually has anything worth
|
|
10
|
-
// repo state — run `polyrepo switch-master`
|
|
11
|
-
// through pMap across many repos at once
|
|
12
|
-
// — this builds the bump checkbox's
|
|
13
|
-
// git calls per repo, 17 times over,
|
|
14
|
-
// prompt even appeared.
|
|
6
|
+
// Summarizes what changed on the local default branch since the last `v*`
|
|
7
|
+
// tag (or the last few commits if there's no tag yet), so the bump
|
|
8
|
+
// checklist can show whether a package actually has anything worth
|
|
9
|
+
// releasing. Reflects local repo state — run `polyrepo switch-master`
|
|
10
|
+
// first if it might be stale. Run through pMap across many repos at once
|
|
11
|
+
// (see describeRecentChangesForAll) — this builds the bump checkbox's
|
|
12
|
+
// preview, and waiting on 3 sequential git calls per repo, 17 times over,
|
|
13
|
+
// added up to a real pause before the prompt even appeared.
|
|
15
14
|
async function describeRecentChangesAsync(repo) {
|
|
16
|
-
const
|
|
15
|
+
const branch = repo.defaultBranch
|
|
16
|
+
const tagResult = await gitAsync(repo.path, ['describe', '--tags', '--abbrev=0', '--match', 'v*', branch])
|
|
17
17
|
const sinceTag = tagResult.ok ? tagResult.stdout : null
|
|
18
|
-
const range = sinceTag ? `${sinceTag}..${
|
|
18
|
+
const range = sinceTag ? `${sinceTag}..${branch}` : branch
|
|
19
19
|
|
|
20
20
|
const [countResult, logResult] = await Promise.all([
|
|
21
21
|
gitAsync(repo.path, ['rev-list', '--count', range]),
|
|
@@ -34,9 +34,9 @@ export function describeRecentChangesForAll(repos, concurrency) {
|
|
|
34
34
|
// Uncapped commit list from the last `v*` tag to HEAD — used right before
|
|
35
35
|
// committing a version bump (see bump.js) to draft a CHANGELOG.md entry, so
|
|
36
36
|
// unlike describeRecentChanges (capped for a preview) this needs the whole
|
|
37
|
-
// list. Uses HEAD rather than
|
|
38
|
-
// exactly where that matters: after checking out the bump
|
|
39
|
-
// HEAD is
|
|
37
|
+
// list. Uses HEAD rather than the repo's default branch because it's
|
|
38
|
+
// called from exactly where that matters: after checking out the bump
|
|
39
|
+
// branch, whose HEAD is the default branch's tip at that point anyway.
|
|
40
40
|
export function fullCommitLinesSince(repo) {
|
|
41
41
|
const tagResult = git(repo.path, ['describe', '--tags', '--abbrev=0', '--match', 'v*', 'HEAD'], { quiet: true })
|
|
42
42
|
const sinceTag = tagResult.ok ? tagResult.stdout : null
|
package/src/commands/bump.js
CHANGED
|
@@ -2,7 +2,7 @@ import fs from 'node:fs'
|
|
|
2
2
|
import { confirm } from '@inquirer/prompts'
|
|
3
3
|
import pc from 'picocolors'
|
|
4
4
|
import { discoverRepos, inspectRepos, readPackageJson } from '../repos.js'
|
|
5
|
-
import {
|
|
5
|
+
import { bumpBranchName } from '../config.js'
|
|
6
6
|
import { loadConfig } from '../loadConfig.js'
|
|
7
7
|
import { bumpVersion, replaceVersionInText } from '../version.js'
|
|
8
8
|
import { git } from '../exec.js'
|
|
@@ -84,7 +84,7 @@ export async function bumpCommand({
|
|
|
84
84
|
|
|
85
85
|
if (!yes) {
|
|
86
86
|
const proceed = await confirm({
|
|
87
|
-
message: `Bump ${selected.length} package(s), open a PR, and merge each into
|
|
87
|
+
message: `Bump ${selected.length} package(s), open a PR, and merge each into its default branch?${
|
|
88
88
|
dryRun ? ' (dry run — no changes will actually be pushed)' : ''
|
|
89
89
|
}`,
|
|
90
90
|
default: true,
|
|
@@ -113,7 +113,7 @@ async function bumpOne(repo, { dryRun, waitChecks }) {
|
|
|
113
113
|
|
|
114
114
|
const syncResult = syncMaster(repo)
|
|
115
115
|
if (!syncResult.ok) return fail(syncResult.message)
|
|
116
|
-
ok(`${
|
|
116
|
+
ok(`${repo.defaultBranch} is up to date.`)
|
|
117
117
|
|
|
118
118
|
const branchName = bumpBranchName(repo.newVersion)
|
|
119
119
|
const state = detectBumpState(repo, branchName)
|
|
@@ -172,7 +172,7 @@ async function bumpOne(repo, { dryRun, waitChecks }) {
|
|
|
172
172
|
|
|
173
173
|
if (state.status !== 'open') {
|
|
174
174
|
const prResult = createPr(repo, {
|
|
175
|
-
base:
|
|
175
|
+
base: repo.defaultBranch,
|
|
176
176
|
branch: branchName,
|
|
177
177
|
title: `chore: bump version to ${repo.newVersion}`,
|
|
178
178
|
body: `Bump version: ${repo.version} → ${repo.newVersion}.`,
|
|
@@ -195,9 +195,9 @@ async function bumpOne(repo, { dryRun, waitChecks }) {
|
|
|
195
195
|
|
|
196
196
|
const resyncResult = syncMaster(repo)
|
|
197
197
|
if (!resyncResult.ok) return fail(resyncResult.message)
|
|
198
|
-
ok(`Local ${
|
|
198
|
+
ok(`Local ${repo.defaultBranch} synced to origin at ${repo.newVersion}.`)
|
|
199
199
|
} else {
|
|
200
|
-
ok(`Already merged as PR #${state.pr.number} — ${
|
|
200
|
+
ok(`Already merged as PR #${state.pr.number} — ${repo.defaultBranch} already has it.`)
|
|
201
201
|
}
|
|
202
202
|
|
|
203
203
|
const tag = tagName(repo.newVersion)
|
|
@@ -213,8 +213,9 @@ async function bumpOne(repo, { dryRun, waitChecks }) {
|
|
|
213
213
|
// Reuse a local branch left over from a previous attempt if there is one,
|
|
214
214
|
// otherwise track the remote branch if a previous attempt got as far as
|
|
215
215
|
// pushing it, otherwise create it fresh from the current (just-synced)
|
|
216
|
-
//
|
|
217
|
-
// to call again after any partial failure without any state
|
|
216
|
+
// default branch. Trying "reuse" first before falling back is what makes
|
|
217
|
+
// this safe to call again after any partial failure without any state
|
|
218
|
+
// bookkeeping.
|
|
218
219
|
function checkoutBumpBranch(repo, branchName) {
|
|
219
220
|
if (git(repo.path, ['checkout', branchName], { quiet: true }).ok) {
|
|
220
221
|
return { ok: true, reused: true }
|