gutterpress 0.11.21 → 0.11.22-alpha.1
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 +11 -0
- package/dist/api/index.d.ts +5 -1
- package/dist/api/index.js +67 -46
- package/dist/{build-0neyk5h6.js → build-se85j9ec.js} +16 -10
- package/dist/cli-5553w972.js +75 -0
- package/dist/{cli-b57adna7.js → cli-8hccw62p.js} +9 -201
- package/dist/cli-9vy4b6ec.js +2089 -0
- package/dist/{cli-sqkccnm7.js → cli-d8xjns66.js} +1 -102
- package/dist/{cli-j0drzb6f.js → cli-fbwqv7p4.js} +720 -5428
- package/dist/cli-gc9av5hs.js +103 -0
- package/dist/cli-gmm3bdeq.js +196 -0
- package/dist/cli-h3phdstf.js +200 -0
- package/dist/{index-qf7y4d85.js → cli-habkc5ng.js} +12 -258
- package/dist/cli-pfrg1r9x.js +3073 -0
- package/dist/{cli-j79451n6.js → cli-yejbb9f8.js} +5 -76
- package/dist/cli.js +15 -13
- package/dist/{doctor-fptq8tvn.js → doctor-bv2t82zd.js} +7 -4
- package/dist/{engine-x07q927h.js → engine-h4ehmrvf.js} +4 -3
- package/dist/{engine-0b8bt05n.js → engine-qwkyhrej.js} +3 -2
- package/dist/{ext-gp624t5c.js → ext-a8ndp142.js} +21 -14
- package/dist/extension-restore-926mv2qk.js +16 -0
- package/dist/extension-restore-q1f7njec.js +20 -0
- package/dist/index-38e18a7j.js +3244 -0
- package/dist/{index-mr72vt8n.js → index-5h9a38jn.js} +1 -213
- package/dist/{cli-5qr3z0qa.js → index-93pqkvty.js} +63 -207
- package/dist/index-dhknsvfz.js +213 -0
- package/dist/index-dm7hx46j.js +2208 -0
- package/dist/{index-t7r8g75c.js → index-gmfh6n05.js} +4 -2
- package/dist/index-r7jcsk6h.js +200 -0
- package/dist/{index-mr7zs8mv.js → index-tkjvs1gf.js} +4774 -9354
- package/dist/index.js +70 -49
- package/dist/lib/extension-manifest.d.ts +22 -1
- package/dist/lib/markdown/plugins.d.ts +20 -0
- package/dist/lib/markdown/two-versions.d.ts +32 -0
- package/dist/lib/publish/plugin-provider.d.ts +16 -0
- package/dist/lib/publish/run-publish.d.ts +13 -5
- package/dist/lib/publish/types.d.ts +34 -2
- package/dist/lib/text-diff.d.ts +43 -0
- package/dist/lib/two-versions-markers.d.ts +26 -0
- package/dist/lib/version-changes.d.ts +87 -0
- package/dist/{new-dre6aw05.js → new-nadp2hvb.js} +23 -16
- package/dist/plugins-gy4q9vaa.js +26 -0
- package/dist/plugins-yeh4shd7.js +24 -0
- package/dist/{preflight-1k1zkg1p.js → preflight-c6q25xqa.js} +18 -11
- package/dist/{preview-dtrksvxf.js → preview-y8chv4cb.js} +19 -13
- package/dist/{publish-g1ytnrmr.js → publish-3qwvxsxa.js} +40 -23
- package/dist/render.js +120 -0
- package/dist/{source-provider-pzmz0br9.js → source-provider-2r74a7wp.js} +2 -1
- package/dist/{source-provider-qbthnmqw.js → source-provider-qbfknhyy.js} +2 -1
- package/dist/{validate-yrrq8639.js → validate-w108kk87.js} +16 -10
- package/package.json +1 -1
|
@@ -2,7 +2,11 @@ import {
|
|
|
2
2
|
capabilitiesFor,
|
|
3
3
|
detectProjectSource,
|
|
4
4
|
findEnclosingRepoDir
|
|
5
|
-
} from "./
|
|
5
|
+
} from "./cli-v5mp7a6q.js";
|
|
6
|
+
import {
|
|
7
|
+
PLUGINS_DIR,
|
|
8
|
+
VENDORED_NPM_DIR
|
|
9
|
+
} from "./cli-h3phdstf.js";
|
|
6
10
|
|
|
7
11
|
// src/lib/git-fs.ts
|
|
8
12
|
import * as nodeFs from "node:fs";
|
|
@@ -82,7 +86,7 @@ var gitFs = {
|
|
|
82
86
|
};
|
|
83
87
|
|
|
84
88
|
// src/lib/source-provider.ts
|
|
85
|
-
import
|
|
89
|
+
import path2 from "node:path";
|
|
86
90
|
import git from "isomorphic-git";
|
|
87
91
|
|
|
88
92
|
// src/lib/remote-auth/operation-log.ts
|
|
@@ -99,52 +103,6 @@ function sanitizeLogText(value, secrets = []) {
|
|
|
99
103
|
}
|
|
100
104
|
return text.replace(/\/\/[^/\s]*@/g, "//").replace(/([?&](?:access_token|token|password|secret|key|code)=)[^&#\s]*/gi, "$1[redacted]").replace(/\b(Bearer|Basic)\s+[A-Za-z0-9+/_.=~-]+/gi, "$1 [redacted]").replace(/\b(?:gh[pousr]_[A-Za-z0-9_]+|github_pat_[A-Za-z0-9_]+)/g, "[redacted]").replace(/[\s\x00-\x1f\x7f]+/g, " ").trim().slice(0, 6000);
|
|
101
105
|
}
|
|
102
|
-
function errorLogData(error) {
|
|
103
|
-
const out = { error: error == null ? String(error) : undefined };
|
|
104
|
-
let current = error;
|
|
105
|
-
const seen = new Set;
|
|
106
|
-
for (let depth = 0;depth < 3 && current != null && !seen.has(current); depth++) {
|
|
107
|
-
seen.add(current);
|
|
108
|
-
const prefix = depth === 0 ? "" : `cause${depth}_`;
|
|
109
|
-
if (typeof current !== "object") {
|
|
110
|
-
out[`${prefix}error`] = String(current);
|
|
111
|
-
break;
|
|
112
|
-
}
|
|
113
|
-
const err = current;
|
|
114
|
-
out[`${prefix}error`] = typeof err.message === "string" ? err.message : "Unknown error";
|
|
115
|
-
for (const key of ["code", "name", "caller", "stack", "syscall", "path"]) {
|
|
116
|
-
if (typeof err[key] === "string")
|
|
117
|
-
out[`${prefix}${key}`] = err[key];
|
|
118
|
-
}
|
|
119
|
-
const data = err.data;
|
|
120
|
-
if (data && typeof data === "object") {
|
|
121
|
-
for (const key of [
|
|
122
|
-
"statusCode",
|
|
123
|
-
"reason",
|
|
124
|
-
"prettyDetails",
|
|
125
|
-
"oid",
|
|
126
|
-
"ref",
|
|
127
|
-
"what",
|
|
128
|
-
"filepath",
|
|
129
|
-
"filepaths",
|
|
130
|
-
"bothModified",
|
|
131
|
-
"deleteByUs",
|
|
132
|
-
"deleteByTheirs"
|
|
133
|
-
]) {
|
|
134
|
-
const value = data[key];
|
|
135
|
-
if (typeof value === "string" || typeof value === "number")
|
|
136
|
-
out[`${prefix}${key}`] = value;
|
|
137
|
-
else if (Array.isArray(value)) {
|
|
138
|
-
out[`${prefix}${key}`] = value.filter((v) => typeof v === "string").slice(0, 50);
|
|
139
|
-
if (value.length > 50)
|
|
140
|
-
out[`${prefix}${key}Count`] = value.length;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
current = err.cause;
|
|
145
|
-
}
|
|
146
|
-
return out;
|
|
147
|
-
}
|
|
148
106
|
var noop = {
|
|
149
107
|
debug: () => {},
|
|
150
108
|
info: () => {},
|
|
@@ -177,11 +135,6 @@ function createFileLogger(logFile, operation, minLevel = "debug", options = {})
|
|
|
177
135
|
error: (step, message, data) => write("error", step, message, data)
|
|
178
136
|
};
|
|
179
137
|
}
|
|
180
|
-
function resolveLogger(logFile, operation, options = {}) {
|
|
181
|
-
if (!logFile)
|
|
182
|
-
return noop;
|
|
183
|
-
return createFileLogger(logFile, operation, "debug", options);
|
|
184
|
-
}
|
|
185
138
|
function formatData(data, clean) {
|
|
186
139
|
if (!data)
|
|
187
140
|
return "";
|
|
@@ -198,205 +151,6 @@ function formatData(data, clean) {
|
|
|
198
151
|
return parts.length > 0 ? " " + parts.join(" ") : "";
|
|
199
152
|
}
|
|
200
153
|
|
|
201
|
-
// src/lib/plugin-vendor.ts
|
|
202
|
-
import { lstat, readFile, realpath } from "node:fs/promises";
|
|
203
|
-
import path2 from "node:path";
|
|
204
|
-
import { exports as resolveExports } from "resolve.exports";
|
|
205
|
-
var PLUGINS_DIR = "plugins";
|
|
206
|
-
var VENDORED_NPM_DIR = "npm";
|
|
207
|
-
var NPM_SEGMENT = /^[a-z0-9][a-z0-9._~-]*$/;
|
|
208
|
-
var EXACT_VERSION = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
209
|
-
var WINDOWS_RESERVED = /^(con|prn|aux|nul|clock\$|conin\$|conout\$|com[1-9¹²³]|lpt[1-9¹²³])(?:\..*)?$/i;
|
|
210
|
-
function isValidNpmPackageName(name) {
|
|
211
|
-
if (!name || name.length > 214 || name !== name.toLowerCase())
|
|
212
|
-
return false;
|
|
213
|
-
if (name.startsWith("@")) {
|
|
214
|
-
const parts = name.slice(1).split("/");
|
|
215
|
-
return parts.length === 2 && parts.every((part) => NPM_SEGMENT.test(part));
|
|
216
|
-
}
|
|
217
|
-
return !name.includes("/") && NPM_SEGMENT.test(name);
|
|
218
|
-
}
|
|
219
|
-
function parseNpmPluginSpec(input) {
|
|
220
|
-
const spec = input.trim();
|
|
221
|
-
if (!spec)
|
|
222
|
-
throw new Error("An npm package name is required.");
|
|
223
|
-
let name = spec;
|
|
224
|
-
let selector;
|
|
225
|
-
if (spec.startsWith("@")) {
|
|
226
|
-
const slash = spec.indexOf("/");
|
|
227
|
-
const versionAt = spec.lastIndexOf("@");
|
|
228
|
-
if (slash > 1 && versionAt > slash) {
|
|
229
|
-
name = spec.slice(0, versionAt);
|
|
230
|
-
selector = spec.slice(versionAt + 1);
|
|
231
|
-
}
|
|
232
|
-
} else {
|
|
233
|
-
const versionAt = spec.lastIndexOf("@");
|
|
234
|
-
if (versionAt > 0) {
|
|
235
|
-
name = spec.slice(0, versionAt);
|
|
236
|
-
selector = spec.slice(versionAt + 1);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
if (!isValidNpmPackageName(name)) {
|
|
240
|
-
throw new Error(`"${name}" is not a valid npm package name. Use a name like ` + "markdown-it-highlightjs or @scope/markdown-it-plugin.");
|
|
241
|
-
}
|
|
242
|
-
if (selector !== undefined && !selector) {
|
|
243
|
-
throw new Error(`The package spec "${spec}" is missing a selector after @.`);
|
|
244
|
-
}
|
|
245
|
-
return selector === undefined ? { name } : { name, selector };
|
|
246
|
-
}
|
|
247
|
-
function isExactNpmVersion(version) {
|
|
248
|
-
return EXACT_VERSION.test(version);
|
|
249
|
-
}
|
|
250
|
-
function encodePathPart(value) {
|
|
251
|
-
return encodeURIComponent(value).replace(/[!'()*]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
252
|
-
}
|
|
253
|
-
function vendoredNpmPluginRoot(projectDir, name, version) {
|
|
254
|
-
return path2.join(projectDir, PLUGINS_DIR, VENDORED_NPM_DIR, encodePathPart(name), encodePathPart(version));
|
|
255
|
-
}
|
|
256
|
-
function vendoredNpmPluginPackageDir(installRoot, name) {
|
|
257
|
-
return path2.join(installRoot, "node_modules", ...name.split("/"));
|
|
258
|
-
}
|
|
259
|
-
function toPosixPath(value) {
|
|
260
|
-
return value.split(path2.sep).join("/");
|
|
261
|
-
}
|
|
262
|
-
function assertWindowsSafeRelativePath(relative) {
|
|
263
|
-
if (!relative || relative.includes("\\") || relative.startsWith("/") || /^[a-zA-Z]:/.test(relative)) {
|
|
264
|
-
throw new Error(`Unsafe package path: ${relative || "(empty)"}`);
|
|
265
|
-
}
|
|
266
|
-
const normalized = relative.normalize("NFKC");
|
|
267
|
-
const parts = normalized.split("/");
|
|
268
|
-
if (parts.some((part) => !part || part === "." || part === "..")) {
|
|
269
|
-
throw new Error(`Unsafe package path: ${relative}`);
|
|
270
|
-
}
|
|
271
|
-
for (const part of parts) {
|
|
272
|
-
if (part.length > 255 || /[\u0000-\u001f<>:"|?*]/.test(part) || part.endsWith(".") || part.endsWith(" ") || WINDOWS_RESERVED.test(part)) {
|
|
273
|
-
throw new Error(`Package path is invalid on Windows: ${relative}`);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
function windowsPathKey(relative) {
|
|
278
|
-
return relative.normalize("NFKC").toLowerCase();
|
|
279
|
-
}
|
|
280
|
-
function isContained(root, candidate) {
|
|
281
|
-
const relative = path2.relative(root, candidate);
|
|
282
|
-
return relative === "" || !relative.startsWith("..") && !path2.isAbsolute(relative);
|
|
283
|
-
}
|
|
284
|
-
function object(value) {
|
|
285
|
-
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
286
|
-
}
|
|
287
|
-
async function readPackageJson(packageDir) {
|
|
288
|
-
const packageJsonPath = path2.join(packageDir, "package.json");
|
|
289
|
-
const info = await lstat(packageJsonPath);
|
|
290
|
-
if (!info.isFile() || info.isSymbolicLink()) {
|
|
291
|
-
throw new Error(`Package metadata is not a regular file: ${packageJsonPath}`);
|
|
292
|
-
}
|
|
293
|
-
const parsed = object(JSON.parse(await readFile(packageJsonPath, "utf8")));
|
|
294
|
-
if (!parsed)
|
|
295
|
-
throw new Error(`Package metadata is not an object: ${packageJsonPath}`);
|
|
296
|
-
return parsed;
|
|
297
|
-
}
|
|
298
|
-
function packageResolutionTargets(manifest, subpath, condition) {
|
|
299
|
-
if (manifest.exports !== undefined) {
|
|
300
|
-
try {
|
|
301
|
-
const targets2 = resolveExports(manifest, subpath ? `./${subpath}` : ".", { require: condition === "require" }) ?? [];
|
|
302
|
-
return targets2.map((target) => ({ target, exact: true }));
|
|
303
|
-
} catch {
|
|
304
|
-
return [];
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
if (subpath)
|
|
308
|
-
return [{ target: subpath, exact: false }];
|
|
309
|
-
const targets = [
|
|
310
|
-
...condition === "import" && typeof manifest.module === "string" ? [manifest.module] : [],
|
|
311
|
-
...typeof manifest.main === "string" ? [manifest.main] : [],
|
|
312
|
-
"index.mjs",
|
|
313
|
-
"index.js",
|
|
314
|
-
"index.cjs"
|
|
315
|
-
];
|
|
316
|
-
return [...new Set(targets)].map((target) => ({ target, exact: false }));
|
|
317
|
-
}
|
|
318
|
-
function safePackageTarget(target) {
|
|
319
|
-
if (!target || target.includes("\\") || target.includes("*") || target.includes("?") || target.includes("#")) {
|
|
320
|
-
return null;
|
|
321
|
-
}
|
|
322
|
-
const relative = target.startsWith("./") ? target.slice(2) : target;
|
|
323
|
-
if (!relative || path2.isAbsolute(relative))
|
|
324
|
-
return null;
|
|
325
|
-
assertWindowsSafeRelativePath(relative);
|
|
326
|
-
if (relative.normalize("NFKC").split("/").some((part) => part.toLowerCase() === "node_modules")) {
|
|
327
|
-
return null;
|
|
328
|
-
}
|
|
329
|
-
return relative;
|
|
330
|
-
}
|
|
331
|
-
async function resolveEntryCandidate(packageDir, resolution) {
|
|
332
|
-
const relative = safePackageTarget(resolution.target);
|
|
333
|
-
if (!relative)
|
|
334
|
-
return null;
|
|
335
|
-
const base = path2.resolve(packageDir, ...relative.split("/"));
|
|
336
|
-
if (!isContained(packageDir, base))
|
|
337
|
-
return null;
|
|
338
|
-
const candidates = resolution.exact ? [base] : [
|
|
339
|
-
base,
|
|
340
|
-
`${base}.mjs`,
|
|
341
|
-
`${base}.js`,
|
|
342
|
-
`${base}.cjs`,
|
|
343
|
-
`${base}.json`,
|
|
344
|
-
path2.join(base, "index.mjs"),
|
|
345
|
-
path2.join(base, "index.js"),
|
|
346
|
-
path2.join(base, "index.cjs"),
|
|
347
|
-
path2.join(base, "index.json")
|
|
348
|
-
];
|
|
349
|
-
for (const candidate of candidates) {
|
|
350
|
-
try {
|
|
351
|
-
const info = await lstat(candidate);
|
|
352
|
-
if (!info.isFile() || info.isSymbolicLink())
|
|
353
|
-
continue;
|
|
354
|
-
const extension = path2.extname(candidate);
|
|
355
|
-
if (extension && ![".js", ".mjs", ".cjs", ".json"].includes(extension))
|
|
356
|
-
continue;
|
|
357
|
-
const [realRoot, realCandidate] = await Promise.all([realpath(packageDir), realpath(candidate)]);
|
|
358
|
-
if (!isContained(realRoot, realCandidate))
|
|
359
|
-
continue;
|
|
360
|
-
return toPosixPath(path2.relative(packageDir, realCandidate));
|
|
361
|
-
} catch {}
|
|
362
|
-
}
|
|
363
|
-
return null;
|
|
364
|
-
}
|
|
365
|
-
async function resolvePackageEntry(packageDir, packageJson) {
|
|
366
|
-
const manifest = packageJson ?? await readPackageJson(packageDir);
|
|
367
|
-
for (const condition of ["import", "require"]) {
|
|
368
|
-
for (const target of packageResolutionTargets(manifest, "", condition)) {
|
|
369
|
-
const resolved = await resolveEntryCandidate(packageDir, target);
|
|
370
|
-
if (resolved)
|
|
371
|
-
return resolved;
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
throw new Error("Package has no contained JavaScript entry point.");
|
|
375
|
-
}
|
|
376
|
-
async function resolveVendoredPluginInstallRoot(projectDir, expectedName, expectedVersion) {
|
|
377
|
-
const requestedProjectRoot = path2.resolve(projectDir);
|
|
378
|
-
const requestedInstallRoot = vendoredNpmPluginRoot(requestedProjectRoot, expectedName, expectedVersion);
|
|
379
|
-
let installInfo;
|
|
380
|
-
try {
|
|
381
|
-
installInfo = await lstat(requestedInstallRoot);
|
|
382
|
-
} catch (error) {
|
|
383
|
-
if (error.code === "ENOENT")
|
|
384
|
-
return null;
|
|
385
|
-
throw error;
|
|
386
|
-
}
|
|
387
|
-
if (!installInfo.isDirectory() || installInfo.isSymbolicLink()) {
|
|
388
|
-
throw new Error("Vendor install root is not a normal directory.");
|
|
389
|
-
}
|
|
390
|
-
const [realProjectRoot, installRoot] = await Promise.all([
|
|
391
|
-
realpath(requestedProjectRoot),
|
|
392
|
-
realpath(requestedInstallRoot)
|
|
393
|
-
]);
|
|
394
|
-
if (!isContained(realProjectRoot, installRoot)) {
|
|
395
|
-
throw new Error("Vendor install root resolves outside the book folder.");
|
|
396
|
-
}
|
|
397
|
-
return installRoot;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
154
|
// src/lib/source-provider.ts
|
|
401
155
|
var noopLogger = {
|
|
402
156
|
debug: () => {},
|
|
@@ -414,7 +168,7 @@ var DEFAULT_BRANCH = "main";
|
|
|
414
168
|
var SNAPSHOT_STAGING_MARKER = "gutterpress-snapshot-staging";
|
|
415
169
|
var repoQueues = new Map;
|
|
416
170
|
function repoLockKey(projectDir) {
|
|
417
|
-
const resolved =
|
|
171
|
+
const resolved = path2.resolve(projectDir);
|
|
418
172
|
try {
|
|
419
173
|
return gitFs.realpathSync(resolved);
|
|
420
174
|
} catch {
|
|
@@ -504,7 +258,7 @@ async function listWorkdirChanges(dir, cache = {}) {
|
|
|
504
258
|
if (workdirOid !== stageOid) {
|
|
505
259
|
adds.push(filepath);
|
|
506
260
|
} else {
|
|
507
|
-
const fileBytes = await gitFs.promises.readFile(
|
|
261
|
+
const fileBytes = await gitFs.promises.readFile(path2.join(dir, filepath));
|
|
508
262
|
const { oid: actualOid } = await git.hashBlob({ object: fileBytes });
|
|
509
263
|
if (actualOid !== stageOid)
|
|
510
264
|
adds.push(filepath);
|
|
@@ -775,10 +529,10 @@ function providerFor(source) {
|
|
|
775
529
|
}
|
|
776
530
|
}
|
|
777
531
|
function gitDirFor(projectDir) {
|
|
778
|
-
return
|
|
532
|
+
return path2.join(projectDir, ".git");
|
|
779
533
|
}
|
|
780
534
|
function snapshotStagingMarkerPath(projectDir) {
|
|
781
|
-
return
|
|
535
|
+
return path2.join(gitDirFor(projectDir), SNAPSHOT_STAGING_MARKER);
|
|
782
536
|
}
|
|
783
537
|
var RESTORE_BACKUP_MESSAGE = "Automatic backup before restoring an earlier version";
|
|
784
538
|
var AUTO_SNAPSHOT_MESSAGE = "Automatic snapshot";
|
|
@@ -931,9 +685,9 @@ async function switchBranch(options) {
|
|
|
931
685
|
const changedPaths = fromOid && fromOid !== toOid ? await diffCommitPaths(repoDir, fromOid, toOid, cache) : [];
|
|
932
686
|
return {
|
|
933
687
|
current: branch,
|
|
934
|
-
changedFiles: changedPaths.map((p) =>
|
|
688
|
+
changedFiles: changedPaths.map((p) => path2.join(repoDir, p))
|
|
935
689
|
};
|
|
936
690
|
});
|
|
937
691
|
}
|
|
938
692
|
|
|
939
|
-
export {
|
|
693
|
+
export { 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, listLocalBranches, switchBranch };
|