gutterpress 0.11.23-alpha.5 → 0.11.23-alpha.6
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 +1 -1
- package/dist/api/index.d.ts +4 -2
- package/dist/api/index.js +14 -6
- package/dist/{build-4qv9wvrx.js → build-py643c5a.js} +7 -7
- package/dist/{cli-pth362d8.js → cli-09jj8w5g.js} +18 -16
- package/dist/{cli-a35w7wzg.js → cli-31anr0r9.js} +107 -56
- package/dist/{cli-w97xcpag.js → cli-55br530w.js} +4 -4
- package/dist/{cli-nk0efg2x.js → cli-bnf7eb38.js} +1 -1
- package/dist/{cli-yxfznmsq.js → cli-qzkq9em8.js} +1 -1
- package/dist/{cli-2t3krcfy.js → cli-tf9tw6at.js} +1 -1
- package/dist/{cli-de8ydmm7.js → cli-x777v272.js} +3 -3
- package/dist/cli.js +12 -12
- package/dist/{doctor-5nh3t346.js → doctor-tyrzqphh.js} +3 -3
- package/dist/{ext-nhcep6h6.js → ext-70kt3eft.js} +7 -7
- package/dist/{extension-restore-x3mtn844.js → extension-restore-fjfn1hrk.js} +4 -4
- package/dist/{extension-restore-55kp0ksq.js → extension-restore-whag4sm0.js} +2 -2
- package/dist/{index-9ye22m7r.js → index-1zqkfzcd.js} +3 -3
- package/dist/{index-t3jkavdz.js → index-cmakyf43.js} +3 -3
- package/dist/{index-h6yk5tjx.js → index-cw03e9p4.js} +107 -56
- package/dist/{index-agrkfbkv.js → index-ejbfetdn.js} +241 -69
- package/dist/index.js +14 -6
- package/dist/lib/remote-auth/repair.d.ts +2 -2
- package/dist/lib/remote-auth/revisions.d.ts +60 -0
- package/dist/lib/remote-auth/scorched-earth.d.ts +2 -2
- package/dist/lib/remote-auth/sync-messages.d.ts +28 -27
- package/dist/lib/remote-auth/sync-types.d.ts +3 -0
- package/dist/lib/remote-auth/sync.d.ts +7 -8
- package/dist/lib/remote-auth/transport.d.ts +8 -1
- package/dist/lib/source-provider.d.ts +80 -35
- package/dist/{new-5a4w2vkh.js → new-b27kyr02.js} +10 -10
- package/dist/{plugins-mm8270tn.js → plugins-racab3cj.js} +1 -1
- package/dist/{plugins-cfzf26b8.js → plugins-vpx7c53c.js} +3 -3
- package/dist/{preflight-jma71n0j.js → preflight-akdqz9v8.js} +7 -7
- package/dist/{preview-kmx4h7gv.js → preview-bck9f88w.js} +7 -7
- package/dist/{publish-62843020.js → publish-keyp8rh4.js} +7 -7
- package/dist/render.js +2 -2
- package/dist/{source-provider-knmcam50.js → source-provider-n7vehh6t.js} +15 -3
- package/dist/{source-provider-98k5deda.js → source-provider-tgzet3j5.js} +15 -3
- package/dist/{validate-vvmrd85g.js → validate-z28cvr5x.js} +7 -7
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from "./cli-d8xjns66.js";
|
|
9
9
|
import {
|
|
10
10
|
package_default
|
|
11
|
-
} from "./cli-
|
|
11
|
+
} from "./cli-qzkq9em8.js";
|
|
12
12
|
import {
|
|
13
13
|
EXIT_CODES
|
|
14
14
|
} from "./cli-46ycxe6r.js";
|
|
@@ -20,14 +20,14 @@ import {
|
|
|
20
20
|
import { defineCommand, parseArgs, runMain } from "citty";
|
|
21
21
|
import { statSync } from "node:fs";
|
|
22
22
|
var SUBCOMMANDS = {
|
|
23
|
-
new: () => import("./new-
|
|
24
|
-
preview: () => import("./preview-
|
|
25
|
-
build: () => import("./build-
|
|
26
|
-
publish: () => import("./publish-
|
|
27
|
-
validate: () => import("./validate-
|
|
28
|
-
preflight: () => import("./preflight-
|
|
29
|
-
doctor: () => import("./doctor-
|
|
30
|
-
ext: () => import("./ext-
|
|
23
|
+
new: () => import("./new-b27kyr02.js").then((m) => m.default),
|
|
24
|
+
preview: () => import("./preview-bck9f88w.js").then((m) => m.default),
|
|
25
|
+
build: () => import("./build-py643c5a.js").then((m) => m.default),
|
|
26
|
+
publish: () => import("./publish-keyp8rh4.js").then((m) => m.default),
|
|
27
|
+
validate: () => import("./validate-z28cvr5x.js").then((m) => m.default),
|
|
28
|
+
preflight: () => import("./preflight-akdqz9v8.js").then((m) => m.default),
|
|
29
|
+
doctor: () => import("./doctor-tyrzqphh.js").then((m) => m.default),
|
|
30
|
+
ext: () => import("./ext-70kt3eft.js").then((m) => m.default)
|
|
31
31
|
};
|
|
32
32
|
var VERSION = package_default.version;
|
|
33
33
|
var main = defineCommand({
|
|
@@ -52,7 +52,7 @@ async function preflightRequiredInvocations(rawArgs) {
|
|
|
52
52
|
const [command, ...commandArgs] = rawArgs;
|
|
53
53
|
try {
|
|
54
54
|
if (command === "new") {
|
|
55
|
-
const { newArgs } = await import("./new-
|
|
55
|
+
const { newArgs } = await import("./new-b27kyr02.js");
|
|
56
56
|
rejectUnknownFlags(commandArgs, newArgs, "new");
|
|
57
57
|
const parsed = parseArgs(commandArgs, {
|
|
58
58
|
...newArgs,
|
|
@@ -64,7 +64,7 @@ async function preflightRequiredInvocations(rawArgs) {
|
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
66
66
|
if (command === "preflight") {
|
|
67
|
-
const { preflightArgs } = await import("./preflight-
|
|
67
|
+
const { preflightArgs } = await import("./preflight-akdqz9v8.js");
|
|
68
68
|
rejectUnknownFlags(commandArgs, preflightArgs, "preflight");
|
|
69
69
|
const parsed = parseArgs(commandArgs, {
|
|
70
70
|
...preflightArgs,
|
|
@@ -84,7 +84,7 @@ async function preflightRequiredInvocations(rawArgs) {
|
|
|
84
84
|
}
|
|
85
85
|
if (subcommand.startsWith("-"))
|
|
86
86
|
return;
|
|
87
|
-
const { EXT_SUBCOMMANDS, extAddArgs, extRemoveArgs, extEnableArgs, extDisableArgs } = await import("./ext-
|
|
87
|
+
const { EXT_SUBCOMMANDS, extAddArgs, extRemoveArgs, extEnableArgs, extDisableArgs } = await import("./ext-70kt3eft.js");
|
|
88
88
|
if (!EXT_SUBCOMMANDS.includes(subcommand)) {
|
|
89
89
|
throw new UsageError(`gutterpress ext: unknown command "${subcommand}"`);
|
|
90
90
|
}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getSystemDiagnostics
|
|
3
|
-
} from "./cli-
|
|
3
|
+
} from "./cli-bnf7eb38.js";
|
|
4
4
|
import"./cli-5crpmz42.js";
|
|
5
5
|
import"./cli-asap4t1z.js";
|
|
6
6
|
import {
|
|
7
7
|
log
|
|
8
|
-
} from "./cli-
|
|
8
|
+
} from "./cli-tf9tw6at.js";
|
|
9
9
|
import {
|
|
10
10
|
UsageError,
|
|
11
11
|
rejectExtraPositionals,
|
|
12
12
|
rejectUnknownFlags
|
|
13
13
|
} from "./cli-d8xjns66.js";
|
|
14
|
-
import"./cli-
|
|
14
|
+
import"./cli-qzkq9em8.js";
|
|
15
15
|
import"./cli-46ycxe6r.js";
|
|
16
16
|
import"./cli-37x76zdn.js";
|
|
17
17
|
|
|
@@ -2,14 +2,14 @@ import {
|
|
|
2
2
|
importExtensionFromFile,
|
|
3
3
|
importExtensionFromUrl,
|
|
4
4
|
searchNpmExtensions
|
|
5
|
-
} from "./cli-
|
|
6
|
-
import"./cli-
|
|
5
|
+
} from "./cli-09jj8w5g.js";
|
|
6
|
+
import"./cli-bnf7eb38.js";
|
|
7
7
|
import"./cli-ht1bcqdd.js";
|
|
8
8
|
import"./cli-58fsk6d7.js";
|
|
9
9
|
import"./cli-5crpmz42.js";
|
|
10
10
|
import"./cli-eyyggect.js";
|
|
11
11
|
import"./cli-asap4t1z.js";
|
|
12
|
-
import"./cli-
|
|
12
|
+
import"./cli-31anr0r9.js";
|
|
13
13
|
import"./cli-v5mp7a6q.js";
|
|
14
14
|
import {
|
|
15
15
|
addExtension,
|
|
@@ -18,12 +18,12 @@ import {
|
|
|
18
18
|
removeExtension,
|
|
19
19
|
setExtensionEnabled,
|
|
20
20
|
updateExtensions
|
|
21
|
-
} from "./cli-
|
|
21
|
+
} from "./cli-55br530w.js";
|
|
22
22
|
import {
|
|
23
23
|
isPathSpecifier,
|
|
24
24
|
parseExtensionSpecifier
|
|
25
|
-
} from "./cli-
|
|
26
|
-
import"./cli-
|
|
25
|
+
} from "./cli-x777v272.js";
|
|
26
|
+
import"./cli-tf9tw6at.js";
|
|
27
27
|
import {
|
|
28
28
|
UsageError,
|
|
29
29
|
exitForUsage,
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
rejectUnknownFlags,
|
|
32
32
|
resolveProjectDir
|
|
33
33
|
} from "./cli-d8xjns66.js";
|
|
34
|
-
import"./cli-
|
|
34
|
+
import"./cli-qzkq9em8.js";
|
|
35
35
|
import {
|
|
36
36
|
EXIT_CODES
|
|
37
37
|
} from "./cli-46ycxe6r.js";
|
|
@@ -3,11 +3,11 @@ import {
|
|
|
3
3
|
restoreForCommand,
|
|
4
4
|
restoreOfflineMessage,
|
|
5
5
|
restorePinnedExtensions
|
|
6
|
-
} from "./cli-
|
|
7
|
-
import"./cli-
|
|
8
|
-
import"./cli-
|
|
6
|
+
} from "./cli-55br530w.js";
|
|
7
|
+
import"./cli-x777v272.js";
|
|
8
|
+
import"./cli-tf9tw6at.js";
|
|
9
9
|
import"./cli-d8xjns66.js";
|
|
10
|
-
import"./cli-
|
|
10
|
+
import"./cli-qzkq9em8.js";
|
|
11
11
|
import"./cli-46ycxe6r.js";
|
|
12
12
|
import"./cli-y28j019d.js";
|
|
13
13
|
import"./cli-37x76zdn.js";
|
|
@@ -3,8 +3,8 @@ import {
|
|
|
3
3
|
restoreForCommand,
|
|
4
4
|
restoreOfflineMessage,
|
|
5
5
|
restorePinnedExtensions
|
|
6
|
-
} from "./index-
|
|
7
|
-
import"./index-
|
|
6
|
+
} from "./index-cmakyf43.js";
|
|
7
|
+
import"./index-1zqkfzcd.js";
|
|
8
8
|
import"./index-46ycxe6r.js";
|
|
9
9
|
import"./index-5x42amh8.js";
|
|
10
10
|
import"./index-37x76zdn.js";
|
|
@@ -398,7 +398,7 @@ import { satisfies, validRange } from "semver";
|
|
|
398
398
|
// package.json
|
|
399
399
|
var package_default = {
|
|
400
400
|
name: "gutterpress",
|
|
401
|
-
version: "0.11.23-alpha.
|
|
401
|
+
version: "0.11.23-alpha.6",
|
|
402
402
|
description: "Markdown-to-PDF converter for professional print layout using a native Chromium print engine and Ghostscript.",
|
|
403
403
|
author: "itlackey",
|
|
404
404
|
license: "MPL-2.0",
|
|
@@ -2863,8 +2863,8 @@ function twoVersions(state, startLine, endLine, silent) {
|
|
|
2863
2863
|
open.attrSet("class", "gp-two-versions");
|
|
2864
2864
|
open.map = [startLine, closer + 1];
|
|
2865
2865
|
open.block = true;
|
|
2866
|
-
pushVersion(state, "yours", "Your
|
|
2867
|
-
pushVersion(state, "online", "Online
|
|
2866
|
+
pushVersion(state, "yours", "Your take", startLine + 1, separator);
|
|
2867
|
+
pushVersion(state, "online", "Online take", separator + 1, closer);
|
|
2868
2868
|
const close = state.push("two_versions_close", "div", -1);
|
|
2869
2869
|
close.block = true;
|
|
2870
2870
|
state.parentType = oldParent;
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
readExtensionMeta,
|
|
15
15
|
resolveExtension,
|
|
16
16
|
slugify
|
|
17
|
-
} from "./index-
|
|
17
|
+
} from "./index-1zqkfzcd.js";
|
|
18
18
|
import {
|
|
19
19
|
BuildError,
|
|
20
20
|
EXIT_CODES
|
|
@@ -599,7 +599,7 @@ async function loadManifestWithPath(pathOrDir, opts) {
|
|
|
599
599
|
if (existsSync(p) && statSync(p).isFile()) {
|
|
600
600
|
const raw = await readFile(p, "utf8");
|
|
601
601
|
if (raw.split(/\r?\n/).includes(TWO_VERSIONS_OPEN)) {
|
|
602
|
-
throw new UsageError(`"${p}" has two
|
|
602
|
+
throw new UsageError(`"${p}" has two takes of a setting — yours and the online copy's — left by getting the latest changes. Open it, keep the one you want and delete the marker lines.`);
|
|
603
603
|
}
|
|
604
604
|
try {
|
|
605
605
|
const parsed = parseYaml(raw) ?? {};
|
|
@@ -1983,7 +1983,7 @@ async function vendorNpmExtension(projectDir, input, exportName, options, commit
|
|
|
1983
1983
|
}
|
|
1984
1984
|
const warnings = [...installed.warnings];
|
|
1985
1985
|
await finalizeNpmPluginInstall(installed).catch((error) => {
|
|
1986
|
-
warnings.push(`The extension was installed, but an old
|
|
1986
|
+
warnings.push(`The extension was installed, but an old download could not be removed: ` + `${error instanceof Error ? error.message : String(error)}`);
|
|
1987
1987
|
});
|
|
1988
1988
|
warnings.push(...await retireLegacyCopies(projectDir, installed.name));
|
|
1989
1989
|
try {
|
|
@@ -6,7 +6,8 @@ import {
|
|
|
6
6
|
import {
|
|
7
7
|
capabilitiesFor,
|
|
8
8
|
detectProjectSource,
|
|
9
|
-
findEnclosingRepoDir
|
|
9
|
+
findEnclosingRepoDir,
|
|
10
|
+
syncRemoteFor
|
|
10
11
|
} from "./index-v5mp7a6q.js";
|
|
11
12
|
|
|
12
13
|
// src/lib/git-fs.ts
|
|
@@ -214,6 +215,7 @@ function gitScopeFor(source) {
|
|
|
214
215
|
return source.repoRoot || source.path;
|
|
215
216
|
}
|
|
216
217
|
var HISTORY_PAGE_LIMIT = 100;
|
|
218
|
+
var NO_VERSION_HISTORY = "This book isn't keeping version history yet. Turn on version history first.";
|
|
217
219
|
var DEFAULT_AUTHOR = "gutterpress";
|
|
218
220
|
var DEFAULT_EMAIL = "noreply@gutterpress.local";
|
|
219
221
|
var DEFAULT_BRANCH = "main";
|
|
@@ -385,7 +387,7 @@ class LocalFolderSourceProvider {
|
|
|
385
387
|
async initVersionHistory(options) {
|
|
386
388
|
const dir = options.projectDir;
|
|
387
389
|
if (await findEnclosingRepoDir(dir) !== undefined) {
|
|
388
|
-
throw new Error("This folder is already inside a
|
|
390
|
+
throw new Error("This folder is already inside a book that keeps version history, so " + "Gutterpress won't start a separate one here.");
|
|
389
391
|
}
|
|
390
392
|
return withRepoLock(dir, async () => {
|
|
391
393
|
const cache = {};
|
|
@@ -409,7 +411,7 @@ class LocalFolderSourceProvider {
|
|
|
409
411
|
});
|
|
410
412
|
}
|
|
411
413
|
snapshot() {
|
|
412
|
-
return Promise.reject(new Error(
|
|
414
|
+
return Promise.reject(new Error(NO_VERSION_HISTORY));
|
|
413
415
|
}
|
|
414
416
|
listHistory() {
|
|
415
417
|
return Promise.resolve([]);
|
|
@@ -418,7 +420,7 @@ class LocalFolderSourceProvider {
|
|
|
418
420
|
return Promise.resolve({ entries: [], hasMore: false });
|
|
419
421
|
}
|
|
420
422
|
restore() {
|
|
421
|
-
return Promise.reject(new Error(
|
|
423
|
+
return Promise.reject(new Error(NO_VERSION_HISTORY));
|
|
422
424
|
}
|
|
423
425
|
}
|
|
424
426
|
|
|
@@ -517,7 +519,7 @@ async function snapshotWorkingTreeUnlocked(options) {
|
|
|
517
519
|
const workdirClean = changes.adds.length === 0 && changes.removes.length === 0;
|
|
518
520
|
if (workdirClean && !staleStaging) {
|
|
519
521
|
logger.debug("snapshot", "no changes — skipping");
|
|
520
|
-
throw new Error("
|
|
522
|
+
throw new Error("Nothing new to save — your last version already has everything.");
|
|
521
523
|
}
|
|
522
524
|
if (workdirClean && staleStaging && await stageMatchesHead(dir, cache)) {
|
|
523
525
|
logger.debug("snapshot", "stale marker outlived a completed commit — clearing, not recommitting");
|
|
@@ -555,7 +557,7 @@ async function snapshotWorkingTreeUnlocked(options) {
|
|
|
555
557
|
};
|
|
556
558
|
}
|
|
557
559
|
function isNoChangesError(e) {
|
|
558
|
-
return e instanceof Error && /
|
|
560
|
+
return e instanceof Error && /nothing new to save/i.test(e.message);
|
|
559
561
|
}
|
|
560
562
|
async function countUnversionedChanges(projectDir) {
|
|
561
563
|
const source = await detectProjectSource(projectDir);
|
|
@@ -586,14 +588,13 @@ function gitDirFor(projectDir) {
|
|
|
586
588
|
function snapshotStagingMarkerPath(projectDir) {
|
|
587
589
|
return path2.join(gitDirFor(projectDir), SNAPSHOT_STAGING_MARKER);
|
|
588
590
|
}
|
|
589
|
-
var RESTORE_BACKUP_MESSAGE = "Automatic
|
|
590
|
-
var AUTO_SNAPSHOT_MESSAGE = "Automatic
|
|
591
|
+
var RESTORE_BACKUP_MESSAGE = "Automatic version before restoring";
|
|
592
|
+
var AUTO_SNAPSHOT_MESSAGE = "Automatic version";
|
|
591
593
|
async function restoreVersionWithBackup(options) {
|
|
592
594
|
const { projectDir, id, authorName, authorEmail } = options;
|
|
593
595
|
const source = await detectProjectSource(projectDir);
|
|
594
|
-
if (source.type !== "local-git-folder")
|
|
595
|
-
throw new Error(
|
|
596
|
-
}
|
|
596
|
+
if (source.type !== "local-git-folder")
|
|
597
|
+
throw new Error(NO_VERSION_HISTORY);
|
|
597
598
|
const provider = new LocalGitSourceProvider(source);
|
|
598
599
|
const repoDir = gitScopeFor(source);
|
|
599
600
|
return withRepoLock(repoDir, async () => {
|
|
@@ -610,42 +611,105 @@ async function restoreVersionWithBackup(options) {
|
|
|
610
611
|
try {
|
|
611
612
|
await provider.restoreUnlocked({ projectDir, id });
|
|
612
613
|
} catch (cause) {
|
|
613
|
-
throw new Error(backupId ? "
|
|
614
|
+
throw new Error(backupId ? "Going back to that version didn't finish, but your work is safe — it was " + "saved as an automatic version and appears in your version history." : "Going back to that version didn't finish. Your book files were not changed.", { cause });
|
|
614
615
|
}
|
|
615
616
|
return { restoredId: id, backupId };
|
|
616
617
|
});
|
|
617
618
|
}
|
|
618
|
-
var SWITCH_BRANCH_SNAPSHOT_MESSAGE = "Saved before switching
|
|
619
|
-
async function
|
|
620
|
-
const
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
const repoDir = gitScopeFor(source);
|
|
624
|
-
const [local, current, remotes] = await Promise.all([
|
|
625
|
-
git.listBranches({ fs: gitFs, dir: repoDir }),
|
|
626
|
-
git.currentBranch({ fs: gitFs, dir: repoDir, fullname: false }).catch(() => {
|
|
619
|
+
var SWITCH_BRANCH_SNAPSHOT_MESSAGE = "Saved before switching revision";
|
|
620
|
+
async function readRevisionRefs(dir) {
|
|
621
|
+
const [names, current, remote] = await Promise.all([
|
|
622
|
+
git.listBranches({ fs: gitFs, dir }),
|
|
623
|
+
git.currentBranch({ fs: gitFs, dir, fullname: false }).catch(() => {
|
|
627
624
|
return;
|
|
628
625
|
}),
|
|
629
|
-
|
|
626
|
+
syncRemoteFor(dir)
|
|
630
627
|
]);
|
|
631
|
-
const
|
|
632
|
-
const
|
|
633
|
-
for (const { remote } of remotes) {
|
|
634
|
-
const names = await git.listBranches({ fs: gitFs, dir: repoDir, remote }).catch(() => []);
|
|
635
|
-
for (const name of names) {
|
|
636
|
-
if (name === "HEAD")
|
|
637
|
-
continue;
|
|
638
|
-
if (!localNames.has(name))
|
|
639
|
-
remoteOnly.add(name);
|
|
640
|
-
}
|
|
641
|
-
}
|
|
628
|
+
const tips = async (branches, prefix) => new Map(await Promise.all(branches.map(async (name) => [name, await git.resolveRef({ fs: gitFs, dir, ref: `${prefix}${name}` })])));
|
|
629
|
+
const onlineNames = remote ? (await git.listBranches({ fs: gitFs, dir, remote: remote.remote }).catch(() => [])).filter((name) => name !== "HEAD") : [];
|
|
642
630
|
return {
|
|
643
631
|
current: current ?? null,
|
|
644
|
-
|
|
645
|
-
|
|
632
|
+
local: await tips(names, "refs/heads/"),
|
|
633
|
+
online: await tips(onlineNames, `refs/remotes/${remote?.remote}/`)
|
|
646
634
|
};
|
|
647
635
|
}
|
|
648
|
-
|
|
636
|
+
var ANCESTRY_DEPTH = 1000;
|
|
637
|
+
async function isContained(dir, tip, inTip, cache) {
|
|
638
|
+
if (tip === inTip)
|
|
639
|
+
return true;
|
|
640
|
+
return git.isDescendent({ fs: gitFs, dir, cache, oid: inTip, ancestor: tip, depth: ANCESTRY_DEPTH }).catch(() => false);
|
|
641
|
+
}
|
|
642
|
+
async function listRevisions(dir) {
|
|
643
|
+
const source = await detectProjectSource(dir);
|
|
644
|
+
if (source.type !== "local-git-folder")
|
|
645
|
+
return null;
|
|
646
|
+
const repoDir = gitScopeFor(source);
|
|
647
|
+
const { current, local, online } = await readRevisionRefs(repoDir);
|
|
648
|
+
const cache = {};
|
|
649
|
+
const names = [...new Set([...local.keys(), ...online.keys()])].sort((a, b) => a.localeCompare(b));
|
|
650
|
+
const revisions = await Promise.all(names.map(async (name) => {
|
|
651
|
+
const here = local.get(name);
|
|
652
|
+
const there = online.get(name);
|
|
653
|
+
return {
|
|
654
|
+
name,
|
|
655
|
+
current: name === current,
|
|
656
|
+
where: here && there ? "online" : here ? "this-computer" : "online-only",
|
|
657
|
+
notBackedUp: Boolean(here && there && !await isContained(repoDir, here, there, cache))
|
|
658
|
+
};
|
|
659
|
+
}));
|
|
660
|
+
return { current, revisions };
|
|
661
|
+
}
|
|
662
|
+
function revisionNameFrom(input) {
|
|
663
|
+
return input.trim().replace(/\s+/g, "-");
|
|
664
|
+
}
|
|
665
|
+
var UNUSABLE_REVISION_NAME = /[\u0000- \u007f~^:?*[\\/<>"|]|\.\.|@\{|^[-.]|\.$|\.lock$|^@$/;
|
|
666
|
+
var REVISION_NAME_MAX = 100;
|
|
667
|
+
async function assertFreeRevisionName(repoDir, name) {
|
|
668
|
+
if (!name)
|
|
669
|
+
throw new Error("Give the revision a name.");
|
|
670
|
+
if (name.length > REVISION_NAME_MAX || UNUSABLE_REVISION_NAME.test(name)) {
|
|
671
|
+
throw new Error(`"${name}" can't be a revision name. Use plain words, numbers and dashes, up to ${REVISION_NAME_MAX} characters.`);
|
|
672
|
+
}
|
|
673
|
+
const { local, online } = await readRevisionRefs(repoDir);
|
|
674
|
+
const taken = (names) => [...names].find((n) => n.toLowerCase() === name.toLowerCase());
|
|
675
|
+
const here = taken(local.keys());
|
|
676
|
+
if (here)
|
|
677
|
+
throw new Error(`There's already a revision called "${here}". Pick a different name.`);
|
|
678
|
+
const there = taken(online.keys());
|
|
679
|
+
if (there) {
|
|
680
|
+
throw new Error(`"${there}" is already a revision in the online copy. Switch to it, or pick a different name.`);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
async function saveUnsavedWork(options, cache) {
|
|
684
|
+
try {
|
|
685
|
+
await snapshotWorkingTreeUnlocked({
|
|
686
|
+
projectDir: options.projectDir,
|
|
687
|
+
repoRoot: options.repoDir,
|
|
688
|
+
message: SWITCH_BRANCH_SNAPSHOT_MESSAGE,
|
|
689
|
+
authorName: options.authorName,
|
|
690
|
+
authorEmail: options.authorEmail,
|
|
691
|
+
cache
|
|
692
|
+
});
|
|
693
|
+
} catch (e) {
|
|
694
|
+
if (!isNoChangesError(e))
|
|
695
|
+
throw e;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
async function createRevision(options) {
|
|
699
|
+
const { projectDir, authorName, authorEmail } = options;
|
|
700
|
+
const source = await detectProjectSource(projectDir);
|
|
701
|
+
if (source.type !== "local-git-folder")
|
|
702
|
+
throw new Error(NO_VERSION_HISTORY);
|
|
703
|
+
const repoDir = gitScopeFor(source);
|
|
704
|
+
const name = revisionNameFrom(options.name);
|
|
705
|
+
return withRepoLock(repoDir, async () => {
|
|
706
|
+
await assertFreeRevisionName(repoDir, name);
|
|
707
|
+
await saveUnsavedWork({ projectDir, repoDir, authorName, authorEmail }, {});
|
|
708
|
+
await git.branch({ fs: gitFs, dir: repoDir, ref: name, checkout: true });
|
|
709
|
+
return { current: name };
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
async function resolveRevision(repoDir, branch) {
|
|
649
713
|
const local = await git.resolveRef({ fs: gitFs, dir: repoDir, ref: `refs/heads/${branch}` }).catch(() => {
|
|
650
714
|
return;
|
|
651
715
|
});
|
|
@@ -659,7 +723,7 @@ async function resolveCopy(repoDir, branch) {
|
|
|
659
723
|
if (oid)
|
|
660
724
|
return { oid, remote };
|
|
661
725
|
}
|
|
662
|
-
throw new Error(`Couldn't find a
|
|
726
|
+
throw new Error(`Couldn't find a revision named "${branch}" — it may have been renamed or deleted.`);
|
|
663
727
|
}
|
|
664
728
|
function isCheckoutConflictError(e) {
|
|
665
729
|
return e?.code === "CheckoutConflictError";
|
|
@@ -696,29 +760,16 @@ async function diffCommitPaths(dir, fromOid, toOid, cache) {
|
|
|
696
760
|
async function switchBranch(options) {
|
|
697
761
|
const { projectDir, branch, authorName, authorEmail } = options;
|
|
698
762
|
const source = await detectProjectSource(projectDir);
|
|
699
|
-
if (source.type !== "local-git-folder")
|
|
700
|
-
throw new Error(
|
|
701
|
-
}
|
|
763
|
+
if (source.type !== "local-git-folder")
|
|
764
|
+
throw new Error(NO_VERSION_HISTORY);
|
|
702
765
|
const repoDir = gitScopeFor(source);
|
|
703
766
|
return withRepoLock(repoDir, async () => {
|
|
704
767
|
const cache = {};
|
|
705
|
-
|
|
706
|
-
await snapshotWorkingTreeUnlocked({
|
|
707
|
-
projectDir,
|
|
708
|
-
repoRoot: repoDir,
|
|
709
|
-
message: SWITCH_BRANCH_SNAPSHOT_MESSAGE,
|
|
710
|
-
authorName,
|
|
711
|
-
authorEmail,
|
|
712
|
-
cache
|
|
713
|
-
});
|
|
714
|
-
} catch (e) {
|
|
715
|
-
if (!isNoChangesError(e))
|
|
716
|
-
throw e;
|
|
717
|
-
}
|
|
768
|
+
await saveUnsavedWork({ projectDir, repoDir, authorName, authorEmail }, cache);
|
|
718
769
|
const fromOid = await git.resolveRef({ fs: gitFs, dir: repoDir, ref: "HEAD" }).catch(() => {
|
|
719
770
|
return;
|
|
720
771
|
});
|
|
721
|
-
const { oid: toOid, remote } = await
|
|
772
|
+
const { oid: toOid, remote } = await resolveRevision(repoDir, branch);
|
|
722
773
|
try {
|
|
723
774
|
await git.checkout({
|
|
724
775
|
fs: gitFs,
|
|
@@ -730,7 +781,7 @@ async function switchBranch(options) {
|
|
|
730
781
|
});
|
|
731
782
|
} catch (e) {
|
|
732
783
|
if (isCheckoutConflictError(e)) {
|
|
733
|
-
throw new Error("Couldn't switch
|
|
784
|
+
throw new Error("Couldn't switch revisions — some files changed on disk right as the switch " + "started. Try again.", { cause: e });
|
|
734
785
|
}
|
|
735
786
|
throw e;
|
|
736
787
|
}
|
|
@@ -742,4 +793,4 @@ async function switchBranch(options) {
|
|
|
742
793
|
});
|
|
743
794
|
}
|
|
744
795
|
|
|
745
|
-
export { gitFs, errorLogData, resolveLogger, gitScopeFor, HISTORY_PAGE_LIMIT, withRepoLock, whenGitIdle, __repoLockQueueSizeForTests, resolveGitAuthor, listWorkdirChanges, stageChanges, hasPendingChanges, hasUncommittedChanges, snapshotWorkingTreeUnlocked, isNoChangesError, countUnversionedChanges, providerFor, gitDirFor, snapshotStagingMarkerPath, RESTORE_BACKUP_MESSAGE, AUTO_SNAPSHOT_MESSAGE, restoreVersionWithBackup, SWITCH_BRANCH_SNAPSHOT_MESSAGE,
|
|
796
|
+
export { gitFs, errorLogData, resolveLogger, gitScopeFor, HISTORY_PAGE_LIMIT, NO_VERSION_HISTORY, withRepoLock, whenGitIdle, __repoLockQueueSizeForTests, resolveGitAuthor, listWorkdirChanges, stageChanges, hasPendingChanges, hasUncommittedChanges, snapshotWorkingTreeUnlocked, isNoChangesError, countUnversionedChanges, providerFor, gitDirFor, snapshotStagingMarkerPath, RESTORE_BACKUP_MESSAGE, AUTO_SNAPSHOT_MESSAGE, restoreVersionWithBackup, SWITCH_BRANCH_SNAPSHOT_MESSAGE, readRevisionRefs, isContained, listRevisions, revisionNameFrom, assertFreeRevisionName, createRevision, switchBranch };
|