@kienntpixon/dieu-phoi 0.7.5 → 0.7.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/lib/wtfast.cjs ADDED
@@ -0,0 +1,156 @@
1
+ 'use strict';
2
+ // wt gc (r1 s3, try2): đường đọc NHANH cho quét hàng loạt. Trên máy thật mỗi lệnh git ~1 giây (Windows + AV), nên 109 repo ×
3
+ // vài lệnh/repo/worktree là vài phút. Ở đây: sổ worktree đọc thẳng từ <common>/worktrees/* (không `git worktree list`),
4
+ // tên nhánh từ refs/heads + packed-refs (không git), và "đã vào nhánh bền nào chưa" tính theo lô (vài lệnh/repo) thay vì
5
+ // một `for-each-ref --contains` cho mỗi worktree. Kết quả CHỈ ĐỂ PHÂN LỚP (dry-run); gỡ thật luôn kiểm lại bằng wtSafety đầy đủ.
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const W = require('./worktree.cjs');
9
+
10
+ const NL = String.fromCharCode(10);
11
+ const lines = (t) => String(t).split(NL).map((l) => l.replace(String.fromCharCode(13), '')).filter(Boolean);
12
+
13
+ /** { main, common } không gọi git khi `<dir>/.git` là thư mục (checkout chính); còn lại rơi về W.mainCheckoutOf. */
14
+ function mainOf(dir) {
15
+ try {
16
+ const g = path.join(dir, '.git');
17
+ if (fs.statSync(g).isDirectory()) return { main: dir, common: g };
18
+ } catch (_) { /* rơi về git */ }
19
+ return W.mainCheckoutOf(dir);
20
+ }
21
+
22
+ function readText(p) { try { return fs.readFileSync(p, 'utf8').trim(); } catch (_) { return null; } }
23
+
24
+ /** HEAD của một git-dir: { branch, detached }. */
25
+ function headOf(gitDir) {
26
+ const t = readText(path.join(gitDir, 'HEAD'));
27
+ if (t === null) return { branch: null, detached: false };
28
+ const m = /^ref:\s*refs\/heads\/(.+)$/.exec(t);
29
+ return m ? { branch: m[1], detached: false } : { branch: null, detached: true };
30
+ }
31
+
32
+ /** Sổ worktree như registeredOf nhưng đọc từ <common>/worktrees/*. Mục đầu = checkout chính. */
33
+ function registeredFs(main, common) {
34
+ const h = headOf(common);
35
+ const out = [{ path: main, branch: h.branch, detached: h.detached, locked: null, prunable: false, admin: null }];
36
+ const root = path.join(common, 'worktrees');
37
+ let names = [];
38
+ try { names = fs.readdirSync(root); } catch (_) { return out; }
39
+ for (const n of names) {
40
+ const admin = path.join(root, n);
41
+ const gd = readText(path.join(admin, 'gitdir'));
42
+ if (!gd) continue;
43
+ const p = path.dirname(gd);
44
+ const hh = headOf(admin);
45
+ const lockedTxt = readText(path.join(admin, 'locked'));
46
+ out.push({
47
+ path: p, branch: hh.branch, detached: hh.detached, locked: lockedTxt, prunable: !fs.existsSync(p), admin,
48
+ });
49
+ }
50
+ return out;
51
+ }
52
+
53
+ /** Tên nhánh cục bộ (refs/heads loose + packed-refs), không gọi git. */
54
+ function localHeadNames(common) {
55
+ const names = new Set();
56
+ const walk = (dir, prefix, depth) => {
57
+ let ents = [];
58
+ try { ents = fs.readdirSync(dir, { withFileTypes: true }); } catch (_) { return; }
59
+ for (const e of ents) {
60
+ if (e.isDirectory()) { if (depth < 8) walk(path.join(dir, e.name), `${prefix}${e.name}/`, depth + 1); } else if (!e.name.endsWith('.lock')) names.add(prefix + e.name);
61
+ }
62
+ };
63
+ walk(path.join(common, 'refs', 'heads'), '', 0);
64
+ const packed = readText(path.join(common, 'packed-refs'));
65
+ if (packed) {
66
+ for (const l of lines(packed)) {
67
+ if (l[0] === '#' || l[0] === '^') continue;
68
+ const i = l.indexOf(' refs/heads/');
69
+ if (i > 0) names.add(l.slice(i + ' refs/heads/'.length));
70
+ }
71
+ }
72
+ return [...names];
73
+ }
74
+
75
+ /**
76
+ * Sự kiện của repo cho quét: một lệnh `for-each-ref` (heads + remotes, kèm sha, ngày commit, upstream) rồi mergedOf(branch)
77
+ * theo lô. Chính sách "đích bền" = W.durablePolicy (cùng chính sách với mergedAnywhere): mọi ref không tạm, trừ chính nó và nhánh
78
+ * LÀM VIỆC đang checkout trong worktree liên kết (không upstream); nhánh checkout của checkout chính (vd feat/overhaul) luôn bền;
79
+ * nhánh tích hợp có upstream (feat/rust-poc) bền dù đang checkout ở worktree liên kết. Nhánh được coi là đã merge nếu là tổ tiên của
80
+ * MỘT tip bền bất kỳ: một lệnh `for-each-ref --merged=<tip1> --merged=<tip2>...` (hợp); tên đích chỉ tra vài tip ưu tiên đầu.
81
+ * `reg` (sổ worktree, mục đầu = checkout chính) truyền sẵn thì khỏi đọc lại.
82
+ */
83
+ function makeFacts(main, reg0) {
84
+ const r = W.gitRun(main, ['for-each-ref', '--format=%(refname)%09%(objectname)%09%(committerdate:unix)%09%(upstream)', 'refs/heads', 'refs/remotes']);
85
+ let reg = reg0;
86
+ if (!reg) { try { reg = registeredFs(main, mainOf(main).common); } catch (_) { reg = []; } }
87
+ const heads = new Map(); // tên ngắn nhánh cục bộ -> { sha, ts }
88
+ const rows = []; // { name, ref, sha, local }
89
+ const hasUp = new Set(); // nhánh cục bộ có upstream
90
+ let originHead = null;
91
+ if (r.ok) {
92
+ for (const l of lines(r.out)) {
93
+ const [ref, sha, ts, up] = l.split(String.fromCharCode(9));
94
+ if (!ref || !sha) continue;
95
+ const local = ref.startsWith('refs/heads/');
96
+ if (local) {
97
+ const name = ref.slice('refs/heads/'.length);
98
+ heads.set(name, { sha, ts: Number(ts) || 0 });
99
+ if (up) hasUp.add(name);
100
+ }
101
+ const m = /^refs\/(?:heads|remotes\/[^/]+)\/(.+)$/.exec(ref);
102
+ if (!m) continue;
103
+ if (m[1] === 'HEAD') { originHead = sha; continue; }
104
+ rows.push({ name: m[1], ref, sha, local });
105
+ }
106
+ }
107
+ const ok = W.durablePolicy(reg, (n) => hasUp.has(n), null);
108
+ const durable = rows.filter((d) => ok(d.name, d.local)); // ref bền: nơi một nhánh có thể "đã vào"
109
+ const mainBranch = reg.length && reg[0].branch ? reg[0].branch : null;
110
+ // tip bền, ưu tiên: nhánh checkout chính, origin/HEAD (nếu trỏ vào một ref bền), main/master/develop/dev/trunk, rồi phần còn lại; khử trùng sha.
111
+ const prefer = ['main', 'master', 'develop', 'dev', 'trunk'];
112
+ const tips = [];
113
+ const seen = new Set();
114
+ const addTip = (d) => { if (d && !seen.has(d.sha)) { seen.add(d.sha); tips.push(d); } };
115
+ if (mainBranch) addTip(durable.find((d) => d.name === mainBranch && d.local));
116
+ if (originHead) addTip(durable.find((d) => d.sha === originHead));
117
+ for (const p of prefer) for (const d of durable.filter((x) => x.name === p)) addTip(d);
118
+ for (const d of durable.filter((x) => x.local)) addTip(d);
119
+ for (const d of durable) addTip(d);
120
+ const merged = new Map(); // sha tip -> Set refname cục bộ nằm trong tip
121
+ const containedIn = (tip) => {
122
+ if (!merged.has(tip.sha)) {
123
+ const q = W.gitRun(main, ['for-each-ref', `--merged=${tip.sha}`, '--format=%(refname)', 'refs/heads']);
124
+ merged.set(tip.sha, new Set(q.ok ? lines(q.out) : []));
125
+ }
126
+ return merged.get(tip.sha);
127
+ };
128
+ let anyCache = null; // refname cục bộ nằm trong ÍT NHẤT một tip (một lệnh, nhiều --merged)
129
+ const containedInAny = (list) => {
130
+ if (list.length === 1) return containedIn(list[0]);
131
+ const key = list.map((t) => t.sha).join(',');
132
+ if (anyCache && anyCache.key === key) return anyCache.set;
133
+ const q = W.gitRun(main, ['for-each-ref', ...list.map((t) => `--merged=${t.sha}`), '--format=%(refname)', 'refs/heads']);
134
+ anyCache = { key, set: new Set(q.ok ? lines(q.out) : []) };
135
+ return anyCache.set;
136
+ };
137
+ const mergedOf = (branch) => {
138
+ const none = { merged: false, into: [] };
139
+ const h = heads.get(branch);
140
+ if (!h) return none;
141
+ const into = [];
142
+ for (const d of durable) if (d.name !== branch && d.sha === h.sha && !into.includes(d.name)) into.push(d.name);
143
+ if (!into.length) {
144
+ const cand = tips.filter((t) => t.name !== branch);
145
+ if (cand.length && containedInAny(cand).has(`refs/heads/${branch}`)) {
146
+ // tên đích chỉ để hiển thị: tra tối đa 4 tip ưu tiên (mỗi tip một lệnh git), không thấy thì ghi chung.
147
+ for (const t of cand.slice(0, 4)) if (containedIn(t).has(`refs/heads/${branch}`)) { into.push(t.name); break; }
148
+ if (!into.length) into.push('(nhánh bền khác)');
149
+ }
150
+ }
151
+ return { merged: into.length > 0, into };
152
+ };
153
+ return { ok: r.ok, heads, mergedOf, shaOf: (branch) => (heads.has(branch) ? heads.get(branch).sha : null), commitMs: (branch) => (heads.has(branch) && heads.get(branch).ts ? heads.get(branch).ts * 1000 : null) };
154
+ }
155
+
156
+ module.exports = { mainOf, registeredFs, localHeadNames, makeFacts, headOf };