@lisang233/pi-sync 0.2.0 → 0.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lisang233/pi-sync",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
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/operations.ts CHANGED
@@ -76,7 +76,7 @@ async function computeState(config: SyncConfig, signal?: AbortSignal): Promise<S
76
76
  readMergeBase(config, { signal }),
77
77
  isMergeInProgress({ signal }),
78
78
  ]);
79
- return classifyState(local, remote, base, mergePending);
79
+ return classifyState(local, remote, base, mergePending, config.include);
80
80
  }
81
81
 
82
82
  export async function status(
package/src/status.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { pathMatchesInclude } from "./tree.js";
2
+
1
3
  export type SyncStateLabel =
2
4
  | "unconfigured"
3
5
  | "unknown"
@@ -37,17 +39,29 @@ export interface StateClassify {
37
39
  * `base` is the content map at the merge-base (empty when there is none, e.g.
38
40
  * a brand-new branch with no shared history). A path present in local & remote
39
41
  * with different content and no base is conservatively a divergence.
42
+ *
43
+ * `include` is the include declaration. The remote and base maps are projected
44
+ * onto the include scope before comparison, so out-of-include paths (for
45
+ * example legacy `pi-sync/home/...` remnants from the old single-snapshot
46
+ * architecture) never pollute the state. A path the local side does not contain
47
+ * is never treated as a local change, which fixes the false "ahead" when the
48
+ * remote carries leftover paths the local never synced.
40
49
  */
41
50
  export function classifyState(
42
51
  local: Map<string, string>,
43
52
  remote: Map<string, string>,
44
53
  base: Map<string, string>,
45
54
  mergePending: boolean,
55
+ include: string[],
46
56
  ): StateClassify {
57
+ const inInclude = (p: string) => include.some((entry) => pathMatchesInclude(p, entry));
58
+ const remoteP = new Map([...remote].filter(([p]) => inInclude(p)));
59
+ const baseP = new Map([...base].filter(([p]) => inInclude(p)));
60
+
47
61
  if (mergePending) {
48
- const diverged = [...new Set([...local.keys(), ...remote.keys()])].filter(
49
- (p) => local.get(p) !== remote.get(p),
50
- );
62
+ const diverged = [...new Set([...local.keys(), ...remoteP.keys()])]
63
+ .filter((p) => local.get(p) !== remoteP.get(p))
64
+ .filter(inInclude);
51
65
  return {
52
66
  label: "conflict",
53
67
  ahead: 0,
@@ -58,24 +72,28 @@ export function classifyState(
58
72
  diverged,
59
73
  };
60
74
  }
61
- const paths = [...new Set([...local.keys(), ...remote.keys()])].sort();
75
+ const paths = [...new Set([...local.keys(), ...remoteP.keys()])].filter(inInclude).sort();
62
76
  const localChanged: string[] = [];
63
77
  const remoteChanged: string[] = [];
64
78
  const diverged: string[] = [];
65
79
  for (const filePath of paths) {
66
80
  const localContent = local.get(filePath);
67
- const remoteContent = remote.get(filePath);
81
+ const remoteContent = remoteP.get(filePath);
68
82
  if (localContent === remoteContent) continue;
69
- const baseContent = base.get(filePath);
70
- const localChangedSide = baseContent !== undefined ? localContent !== baseContent : true;
71
- const remoteChangedSide = baseContent !== undefined ? remoteContent !== baseContent : true;
83
+ const baseContent = baseP.get(filePath);
84
+ const baseHas = baseContent !== undefined;
85
+ // A side is "changed" only if it actually holds the path and its content
86
+ // differs from the base (an added or deleted file counts). Without a base
87
+ // (fresh branch) fall back to "the side holds the path".
88
+ const localChangedSide = baseHas ? localContent !== baseContent : localContent !== undefined;
89
+ const remoteChangedSide = baseHas ? remoteContent !== baseContent : remoteContent !== undefined;
72
90
  if (localChangedSide && remoteChangedSide) diverged.push(filePath);
73
91
  else if (localChangedSide) localChanged.push(filePath);
74
92
  else if (remoteChangedSide) remoteChanged.push(filePath);
75
93
  }
76
94
 
77
95
  let label: SyncStateLabel;
78
- if (remote.size === 0) label = "unpublished";
96
+ if (remoteP.size === 0) label = "unpublished";
79
97
  else if (diverged.length > 0 || (localChanged.length > 0 && remoteChanged.length > 0)) {
80
98
  label = "conflict";
81
99
  } else if (remoteChanged.length > 0) {