@lisang233/pi-sync 0.2.3 → 0.2.4
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 +3 -4
- package/package.json +1 -1
- package/src/extension.ts +1 -17
- package/src/git.ts +0 -24
- package/src/operations.ts +2 -36
- package/src/status.ts +1 -1
package/README.md
CHANGED
|
@@ -19,9 +19,9 @@ detection and merging are handled by git itself.
|
|
|
19
19
|
rewritten remote doesn't spuriously conflict.
|
|
20
20
|
- **Git-native three-way merge** — `/sync pull` merges the remote branch into
|
|
21
21
|
the local side. Divergent edits keep local configuration protected while
|
|
22
|
-
holding conflicts in the mirror;
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
holding conflicts in the mirror; `--ours`/`--theirs` or manual edits resolve
|
|
23
|
+
them, and `/sync merge` validates (no markers, valid JSON) before applying to
|
|
24
|
+
local files.
|
|
25
25
|
- **Deliberate commands** — nothing moves your files without being asked;
|
|
26
26
|
`automatic` only observes at session start.
|
|
27
27
|
- **Sensible defaults** — one `pi-sync.json` points at a git remote and branch,
|
|
@@ -48,7 +48,6 @@ pi install npm:@lisang233/pi-sync
|
|
|
48
48
|
/sync status # config + sync state + next step (--diff for content)
|
|
49
49
|
/sync fetch # pull the remote tree without applying
|
|
50
50
|
/sync pull # fetch + merge (--force overwrites local)
|
|
51
|
-
/sync mergetool # launch git mergetool for conflicts ([tool])
|
|
52
51
|
/sync merge # complete merge (--abort, --ours, --theirs)
|
|
53
52
|
/sync push # publish the local tree (--force overwrites remote)
|
|
54
53
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lisang233/pi-sync",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Personal Pi extension that syncs Pi configuration through Git with background auto-sync and git-style fetch/merge conflict handling.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/extension.ts
CHANGED
|
@@ -15,17 +15,7 @@ interface BackgroundSync {
|
|
|
15
15
|
settled: Promise<void>;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
const COMMANDS = [
|
|
19
|
-
"init",
|
|
20
|
-
"status",
|
|
21
|
-
"push",
|
|
22
|
-
"pull",
|
|
23
|
-
"fetch",
|
|
24
|
-
"merge",
|
|
25
|
-
"mergetool",
|
|
26
|
-
"config",
|
|
27
|
-
"help",
|
|
28
|
-
] as const;
|
|
18
|
+
const COMMANDS = ["init", "status", "push", "pull", "fetch", "merge", "config", "help"] as const;
|
|
29
19
|
|
|
30
20
|
type Subcommand = (typeof COMMANDS)[number];
|
|
31
21
|
|
|
@@ -41,7 +31,6 @@ const USAGE = [
|
|
|
41
31
|
" fetch fetch the remote tree without applying",
|
|
42
32
|
" pull fetch + merge (--force overwrites local)",
|
|
43
33
|
" merge complete merge (--abort, --ours, --theirs)",
|
|
44
|
-
" mergetool launch git mergetool for conflicts ([tool])",
|
|
45
34
|
" push publish local tree (--force overwrites remote)",
|
|
46
35
|
" help show this help",
|
|
47
36
|
].join("\n");
|
|
@@ -226,11 +215,6 @@ async function handleCommand(
|
|
|
226
215
|
theirs: restTokens.some((token) => token === "--theirs"),
|
|
227
216
|
});
|
|
228
217
|
return;
|
|
229
|
-
case "mergetool": {
|
|
230
|
-
const tool = restTokens.find((token) => !token.startsWith("-"));
|
|
231
|
-
await operations.mergetool(ctx, config, tool);
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
218
|
case "config":
|
|
235
219
|
await runConfigEditor(ctx.ui, config);
|
|
236
220
|
return;
|
package/src/git.ts
CHANGED
|
@@ -386,30 +386,6 @@ export async function checkoutTheirs(options: GitRunOptions = {}): Promise<void>
|
|
|
386
386
|
await stageAll(options);
|
|
387
387
|
}
|
|
388
388
|
|
|
389
|
-
/**
|
|
390
|
-
* Launch git mergetool interactively in the mirror work tree.
|
|
391
|
-
*/
|
|
392
|
-
export async function launchMergetool(tool?: string, options: GitRunOptions = {}): Promise<void> {
|
|
393
|
-
throwIfAborted(options.signal);
|
|
394
|
-
const repo = gitCwd();
|
|
395
|
-
const args = ["mergetool", "-y"];
|
|
396
|
-
if (tool && tool.trim().length > 0) {
|
|
397
|
-
args.push(`--tool=${tool.trim()}`);
|
|
398
|
-
}
|
|
399
|
-
await new Promise<void>((resolve, reject) => {
|
|
400
|
-
const child = spawn("git", args, {
|
|
401
|
-
cwd: repo,
|
|
402
|
-
stdio: process.stdin.isTTY ? "inherit" : ["ignore", "pipe", "pipe"],
|
|
403
|
-
windowsHide: false,
|
|
404
|
-
});
|
|
405
|
-
child.on("error", reject);
|
|
406
|
-
child.on("close", (code) => {
|
|
407
|
-
if (code === 0) resolve();
|
|
408
|
-
else reject(new Error(`git mergetool exited with status ${code ?? "unknown"}.`));
|
|
409
|
-
});
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
|
-
|
|
413
389
|
/** Abort an in-progress merge, restoring the work tree to the pre-merge state. */
|
|
414
390
|
export async function abortMerge(options: GitRunOptions = {}): Promise<void> {
|
|
415
391
|
await runGit(["merge", "--abort"], { cwd: gitCwd(), signal: options.signal });
|
package/src/operations.ts
CHANGED
|
@@ -20,7 +20,6 @@ import {
|
|
|
20
20
|
ensureMirror,
|
|
21
21
|
fetchRemote,
|
|
22
22
|
isMergeInProgress,
|
|
23
|
-
launchMergetool,
|
|
24
23
|
listConflictedPaths,
|
|
25
24
|
mergeRemote,
|
|
26
25
|
pushBranch,
|
|
@@ -126,7 +125,7 @@ function nextStepHint(info: StateClassify): string {
|
|
|
126
125
|
case "behind":
|
|
127
126
|
return "next: /sync pull to fetch and apply remote changes";
|
|
128
127
|
case "conflict":
|
|
129
|
-
return "next: /sync merge (--ours
|
|
128
|
+
return "next: /sync merge (--ours or --theirs), or /sync pull --force to overwrite local";
|
|
130
129
|
case "unknown":
|
|
131
130
|
return "next: /sync fetch to check the remote";
|
|
132
131
|
}
|
|
@@ -229,7 +228,7 @@ export async function pull(
|
|
|
229
228
|
// ignore
|
|
230
229
|
}
|
|
231
230
|
const conflictList = conflicts.length > 0 ? ` (${conflicts.join(", ")})` : "";
|
|
232
|
-
const message = `Merge conflict in ${conflicts.length} file(s)${conflictList}. Local configuration in ${agentDir()} was preserved.
|
|
231
|
+
const message = `Merge conflict in ${conflicts.length} file(s)${conflictList}. Local configuration in ${agentDir()} was preserved. Edit files or resolve with /sync merge --ours / --theirs. Then run /sync merge. (remote ${shortId(
|
|
233
232
|
remoteRevision,
|
|
234
233
|
)})`;
|
|
235
234
|
ctx.ui.notify(message, "warning");
|
|
@@ -415,7 +414,6 @@ export async function merge(
|
|
|
415
414
|
"Or choose a resolution strategy:",
|
|
416
415
|
" /sync merge --ours (keep local configuration)",
|
|
417
416
|
" /sync merge --theirs (use remote configuration)",
|
|
418
|
-
" /sync mergetool (open 3-way merge tool)",
|
|
419
417
|
" /sync merge --abort (discard merge and restore)",
|
|
420
418
|
].join("\n");
|
|
421
419
|
ctx.ui.notify(message, "error");
|
|
@@ -432,38 +430,6 @@ export async function merge(
|
|
|
432
430
|
return { pushed: false, pulled: false, merged: true, message };
|
|
433
431
|
}
|
|
434
432
|
|
|
435
|
-
export async function mergetool(
|
|
436
|
-
ctx: CommandContext,
|
|
437
|
-
config: SyncConfig,
|
|
438
|
-
tool?: string,
|
|
439
|
-
): Promise<SyncResult> {
|
|
440
|
-
if (!(await isMergeInProgress({ signal: ctx.signal }))) {
|
|
441
|
-
const message =
|
|
442
|
-
"No merge in progress. Run /sync pull to merge remote changes into local files.";
|
|
443
|
-
ctx.ui.notify(message, "info");
|
|
444
|
-
return { pushed: false, pulled: false, merged: false, message };
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
try {
|
|
448
|
-
await launchMergetool(tool, { signal: ctx.signal });
|
|
449
|
-
await refreshIndicator(ctx, config);
|
|
450
|
-
const conflicts = await listConflictedPaths({ signal: ctx.signal });
|
|
451
|
-
if (conflicts.length === 0) {
|
|
452
|
-
ctx.ui.notify("Mergetool finished. Run /sync merge to validate and apply.", "info");
|
|
453
|
-
} else {
|
|
454
|
-
ctx.ui.notify(
|
|
455
|
-
`Mergetool finished, but ${conflicts.length} file(s) still conflicted.`,
|
|
456
|
-
"warning",
|
|
457
|
-
);
|
|
458
|
-
}
|
|
459
|
-
return { pushed: false, pulled: false, merged: false, message: "mergetool" };
|
|
460
|
-
} catch (error) {
|
|
461
|
-
const message = `Failed to run mergetool: ${error instanceof Error ? error.message : String(error)}`;
|
|
462
|
-
ctx.ui.notify(message, "error");
|
|
463
|
-
return { pushed: false, pulled: false, merged: false, message };
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
|
|
467
433
|
function describeChanges(summary: ReturnType<typeof diffSummary>): string {
|
|
468
434
|
const parts: string[] = [];
|
|
469
435
|
if (summary.added > 0) parts.push(`${summary.added} added`);
|
package/src/status.ts
CHANGED
|
@@ -135,6 +135,6 @@ export function syncIndicatorText(info: SyncStatusInfo): string {
|
|
|
135
135
|
case "behind":
|
|
136
136
|
return `sync: ${info.behind} behind — pull`;
|
|
137
137
|
case "conflict":
|
|
138
|
-
return `sync: conflict (${info.conflicts}) — /sync merge
|
|
138
|
+
return `sync: conflict (${info.conflicts}) — /sync merge`;
|
|
139
139
|
}
|
|
140
140
|
}
|