tether-sync 0.2.0 → 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.
|
@@ -99,7 +99,7 @@ function call(endpoint, method, params = {}, timeoutMs = 10 * 60 * 1e3) {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
// packages/client/src/version.js
|
|
102
|
-
var VERSION = "0.
|
|
102
|
+
var VERSION = "0.6.0";
|
|
103
103
|
|
|
104
104
|
// packages/client/src/update.js
|
|
105
105
|
import fs2 from "node:fs";
|
|
@@ -154,10 +154,33 @@ function npmInstall(spec, prefix) {
|
|
|
154
154
|
// packages/client/src/git.js
|
|
155
155
|
import fs3 from "node:fs";
|
|
156
156
|
import path3 from "node:path";
|
|
157
|
-
import { execFileSync } from "node:child_process";
|
|
157
|
+
import { execFileSync, spawnSync as spawnSync2 } from "node:child_process";
|
|
158
158
|
var GIT_MODES = ["keep", "follow"];
|
|
159
|
+
var gitExe;
|
|
160
|
+
function gitBinary() {
|
|
161
|
+
if (gitExe !== void 0) return gitExe;
|
|
162
|
+
const candidates = ["git"];
|
|
163
|
+
if (isWindows) {
|
|
164
|
+
const pf = [process.env.ProgramFiles, process.env["ProgramFiles(x86)"], process.env.LOCALAPPDATA && path3.join(process.env.LOCALAPPDATA, "Programs")].filter(Boolean);
|
|
165
|
+
for (const p of pf) candidates.push(path3.join(p, "Git", "cmd", "git.exe"));
|
|
166
|
+
const desktop = process.env.LOCALAPPDATA && path3.join(process.env.LOCALAPPDATA, "GitHubDesktop");
|
|
167
|
+
if (desktop && fs3.existsSync(desktop)) {
|
|
168
|
+
for (const app of fs3.readdirSync(desktop).filter((d) => d.startsWith("app-")).sort().reverse()) {
|
|
169
|
+
candidates.push(path3.join(desktop, app, "resources", "app", "git", "cmd", "git.exe"));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
} else candidates.push("/usr/bin/git", "/usr/local/bin/git");
|
|
173
|
+
gitExe = candidates.find((c) => spawnSync2(c, ["--version"], { stdio: "ignore", windowsHide: true }).status === 0) ?? null;
|
|
174
|
+
return gitExe;
|
|
175
|
+
}
|
|
159
176
|
function git(cwd, args) {
|
|
160
|
-
|
|
177
|
+
const exe = gitBinary();
|
|
178
|
+
if (!exe) throw new Error("git is not installed");
|
|
179
|
+
return execFileSync(exe, ["-C", cwd, ...args], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], windowsHide: true, maxBuffer: 16 * 1024 * 1024 }).replace(/\r?\n$/, "");
|
|
180
|
+
}
|
|
181
|
+
function gitOk(cwd, args) {
|
|
182
|
+
const exe = gitBinary();
|
|
183
|
+
return !!exe && spawnSync2(exe, ["-C", cwd, ...args], { stdio: "ignore", windowsHide: true }).status === 0;
|
|
161
184
|
}
|
|
162
185
|
function findRepo(p) {
|
|
163
186
|
let dir = path3.resolve(p);
|
|
@@ -166,11 +189,23 @@ function findRepo(p) {
|
|
|
166
189
|
if (up === dir) return null;
|
|
167
190
|
dir = up;
|
|
168
191
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
192
|
+
if (gitBinary()) {
|
|
193
|
+
try {
|
|
194
|
+
const [root, gitDir] = git(dir, ["rev-parse", "--show-toplevel", "--absolute-git-dir"]).split(/\r?\n/);
|
|
195
|
+
return { root: path3.resolve(root), gitDir: path3.resolve(gitDir) };
|
|
196
|
+
} catch {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
for (let d = dir; ; d = path3.dirname(d)) {
|
|
201
|
+
const dot = path3.join(d, ".git");
|
|
202
|
+
const st = fs3.existsSync(dot) ? fs3.statSync(dot) : null;
|
|
203
|
+
if (st?.isDirectory()) return { root: d, gitDir: dot };
|
|
204
|
+
if (st?.isFile()) {
|
|
205
|
+
const m = fs3.readFileSync(dot, "utf8").match(/^gitdir:\s*(.+)$/m);
|
|
206
|
+
if (m) return { root: d, gitDir: path3.resolve(d, m[1].trim()) };
|
|
207
|
+
}
|
|
208
|
+
if (path3.dirname(d) === d) return null;
|
|
174
209
|
}
|
|
175
210
|
}
|
|
176
211
|
function gitBusy(gitDir) {
|
|
@@ -183,47 +218,135 @@ function gitBusy(gitDir) {
|
|
|
183
218
|
if (has("REVERT_HEAD")) return "revert in progress";
|
|
184
219
|
return null;
|
|
185
220
|
}
|
|
186
|
-
var
|
|
187
|
-
var
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
221
|
+
var rel = (root, abs) => path3.relative(root, abs).split(path3.sep).join("/");
|
|
222
|
+
var head = (root) => {
|
|
223
|
+
try {
|
|
224
|
+
return git(root, ["rev-parse", "--verify", "-q", "HEAD"]);
|
|
225
|
+
} catch {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
function fileBlob(root, abs) {
|
|
230
|
+
try {
|
|
231
|
+
return git(root, ["-c", "filter.tether-keep.clean=", "hash-object", `--path=${rel(root, abs)}`, "--", abs]);
|
|
232
|
+
} catch {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function blobAt(root, rev, abs) {
|
|
237
|
+
try {
|
|
238
|
+
return git(root, ["rev-parse", "--verify", "-q", `${rev}:${rel(root, abs)}`]);
|
|
239
|
+
} catch {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function indexBlob(root, abs) {
|
|
244
|
+
try {
|
|
245
|
+
const line = git(root, ["ls-files", "-s", "--", rel(root, abs)]);
|
|
246
|
+
return line ? line.split(/\s+/)[1] : null;
|
|
247
|
+
} catch {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function changeFromGit(root, abs) {
|
|
252
|
+
const blob = fileBlob(root, abs);
|
|
253
|
+
if (!blob) return { fromGit: false };
|
|
254
|
+
const h = head(root);
|
|
255
|
+
if (h && blobAt(root, "HEAD", abs) === blob) return { fromGit: true, commit: h };
|
|
256
|
+
if (indexBlob(root, abs) === blob) return { fromGit: true, commit: null };
|
|
257
|
+
return { fromGit: false };
|
|
258
|
+
}
|
|
259
|
+
var isTracked = (root, abs) => gitOk(root, ["ls-files", "--error-unmatch", "--", rel(root, abs)]);
|
|
260
|
+
var FILTER = "tether-keep";
|
|
261
|
+
var CLEAN = `sh -c 'git cat-file blob ":$1" 2>/dev/null || cat' -- %f`;
|
|
262
|
+
function attributesFile(root) {
|
|
263
|
+
return path3.resolve(root, git(root, ["rev-parse", "--git-path", "info/attributes"]));
|
|
264
|
+
}
|
|
265
|
+
function attrLine(relPath) {
|
|
266
|
+
const pattern = /[\s"\\]/.test(relPath) ? `"/${relPath.replace(/["\\]/g, "\\$&")}"` : `/${relPath.replace(/[*?[\]!]/g, "\\$&")}`;
|
|
267
|
+
return `${pattern} filter=${FILTER}`;
|
|
268
|
+
}
|
|
269
|
+
var readLines = (file) => fs3.existsSync(file) ? fs3.readFileSync(file, "utf8").split(/\r?\n/).filter((l) => l !== "") : [];
|
|
270
|
+
function setKeep(root, abs, on) {
|
|
271
|
+
if (!gitBinary()) return false;
|
|
272
|
+
try {
|
|
273
|
+
const file = attributesFile(root);
|
|
274
|
+
const line = attrLine(rel(root, abs));
|
|
275
|
+
const lines = readLines(file);
|
|
276
|
+
const has = lines.includes(line);
|
|
277
|
+
if (on) {
|
|
278
|
+
git(root, ["config", `filter.${FILTER}.clean`, CLEAN]);
|
|
279
|
+
git(root, ["config", `filter.${FILTER}.required`, "false"]);
|
|
280
|
+
if (!has) {
|
|
281
|
+
fs3.mkdirSync(path3.dirname(file), { recursive: true });
|
|
282
|
+
fs3.writeFileSync(file, [...lines, line].join("\n") + "\n");
|
|
283
|
+
}
|
|
284
|
+
restage(root, abs);
|
|
285
|
+
} else if (has) {
|
|
286
|
+
const rest = lines.filter((l) => l !== line);
|
|
287
|
+
fs3.writeFileSync(file, rest.length ? rest.join("\n") + "\n" : "");
|
|
288
|
+
}
|
|
289
|
+
return true;
|
|
290
|
+
} catch {
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function isKept(root, abs) {
|
|
295
|
+
try {
|
|
296
|
+
return readLines(attributesFile(root)).includes(attrLine(rel(root, abs)));
|
|
297
|
+
} catch {
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
function restage(root, abs) {
|
|
302
|
+
if (isTracked(root, abs)) gitOk(root, ["add", "--", rel(root, abs)]);
|
|
303
|
+
}
|
|
304
|
+
var isAncestor = (root, commit) => !!commit && gitOk(root, ["merge-base", "--is-ancestor", commit, "HEAD"]);
|
|
305
|
+
function changesBetween(root, from, to) {
|
|
306
|
+
try {
|
|
307
|
+
const out = git(root, ["-c", "core.quotePath=false", "log", "--reverse", "--first-parent", "--format=%x01", "--name-status", "-M", `${from}..${to}`]);
|
|
308
|
+
const renames = /* @__PURE__ */ new Map();
|
|
309
|
+
const added = /* @__PURE__ */ new Set();
|
|
310
|
+
for (const line of out.split(/\r?\n/)) {
|
|
311
|
+
const parts = line.split(" ");
|
|
312
|
+
if (parts.length < 2) continue;
|
|
313
|
+
const status = parts[0][0];
|
|
314
|
+
if (status === "R") {
|
|
315
|
+
const [, oldP, newP] = parts;
|
|
316
|
+
const origin = [...renames.entries()].find(([, cur]) => cur === oldP)?.[0] ?? oldP;
|
|
317
|
+
if (added.delete(oldP)) added.add(newP);
|
|
318
|
+
else renames.set(origin, newP);
|
|
319
|
+
} else if (status === "A") added.add(parts[1]);
|
|
320
|
+
else if (status === "D") {
|
|
321
|
+
added.delete(parts[1]);
|
|
322
|
+
for (const [o, cur] of renames) if (cur === parts[1]) renames.delete(o);
|
|
323
|
+
}
|
|
209
324
|
}
|
|
325
|
+
const abs = (p) => path3.join(root, ...p.split("/"));
|
|
326
|
+
return [
|
|
327
|
+
...[...renames].map(([o, n]) => ({ status: "R", from: abs(o), to: abs(n) })),
|
|
328
|
+
...[...added].map((p) => ({ status: "A", from: abs(p), to: abs(p) }))
|
|
329
|
+
];
|
|
330
|
+
} catch {
|
|
331
|
+
return [];
|
|
210
332
|
}
|
|
211
333
|
}
|
|
334
|
+
var HOOKS = ["post-merge", "post-checkout", "post-rewrite"];
|
|
335
|
+
var begin = (home) => `# >>> tether (${home.replace(/\\/g, "/")}) >>>`;
|
|
336
|
+
var end = (home) => `# <<< tether (${home.replace(/\\/g, "/")}) <<<`;
|
|
212
337
|
function removeHooks(hooksDir, home) {
|
|
213
338
|
for (const hook of HOOKS) {
|
|
214
339
|
const file = path3.join(hooksDir, hook);
|
|
215
340
|
if (!fs3.existsSync(file)) continue;
|
|
216
|
-
|
|
341
|
+
let text = fs3.readFileSync(file, "utf8");
|
|
342
|
+
const b = text.indexOf(begin(home));
|
|
343
|
+
if (b < 0) continue;
|
|
344
|
+
const e = text.indexOf(end(home), b);
|
|
345
|
+
text = (text.slice(0, b) + (e < 0 ? "" : text.slice(e + end(home).length))).replace(/\n{3,}/g, "\n\n");
|
|
217
346
|
if (/^(#!.*\n)?\s*$/.test(text)) fs3.rmSync(file);
|
|
218
347
|
else fs3.writeFileSync(file, text.replace(/\s*$/, "\n"));
|
|
219
348
|
}
|
|
220
349
|
}
|
|
221
|
-
function stripBlock(text, home) {
|
|
222
|
-
const b = text.indexOf(begin(home));
|
|
223
|
-
if (b < 0) return text;
|
|
224
|
-
const e = text.indexOf(end(home), b);
|
|
225
|
-
return (text.slice(0, b) + (e < 0 ? "" : text.slice(e + end(home).length))).replace(/\n{3,}/g, "\n\n");
|
|
226
|
-
}
|
|
227
350
|
|
|
228
351
|
export {
|
|
229
352
|
isWindows,
|
|
@@ -241,8 +364,19 @@ export {
|
|
|
241
364
|
prefixOf,
|
|
242
365
|
npmInstall,
|
|
243
366
|
GIT_MODES,
|
|
367
|
+
gitBinary,
|
|
244
368
|
findRepo,
|
|
245
369
|
gitBusy,
|
|
246
|
-
|
|
370
|
+
head,
|
|
371
|
+
fileBlob,
|
|
372
|
+
blobAt,
|
|
373
|
+
indexBlob,
|
|
374
|
+
changeFromGit,
|
|
375
|
+
isTracked,
|
|
376
|
+
setKeep,
|
|
377
|
+
isKept,
|
|
378
|
+
restage,
|
|
379
|
+
isAncestor,
|
|
380
|
+
changesBetween,
|
|
247
381
|
removeHooks
|
|
248
382
|
};
|