nano-git 0.2.3 → 0.3.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 +6 -0
- package/dist/workdir/change-index-plan.mjs +76 -0
- package/dist/workdir/change-index.d.mts +21 -0
- package/dist/workdir/change-index.mjs +413 -0
- package/dist/workdir/core.d.mts +54 -22
- package/dist/workdir/directory-view.mjs +197 -0
- package/dist/workdir/dirty-dir-plan.mjs +73 -0
- package/dist/workdir/dirty-dir.d.mts +28 -0
- package/dist/workdir/dirty-dir.mjs +32 -0
- package/dist/workdir/file-backend.mjs +96 -10
- package/dist/workdir/memory-backend.mjs +31 -12
- package/dist/workdir/nodes.mjs +1 -7
- package/dist/workdir/session-internal.mjs +216 -9
- package/dist/workdir/session-transaction.mjs +115 -0
- package/dist/workdir/session.mjs +103 -230
- package/dist/workdir/sqlite-backend.mjs +149 -48
- package/dist/workdir/state-store.d.mts +19 -8
- package/dist/workdir/write-tree.mjs +104 -40
- package/package.json +160 -199
- package/dist/workdir/change-log.d.mts +0 -25
- package/dist/workdir/change-log.mjs +0 -69
package/dist/workdir/session.mjs
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { VirtualNotDirectoryError, VirtualNotFileError, VirtualNotSymlinkError, VirtualPathAlreadyExistsError, VirtualPathNotFoundError, VirtualRevertNotSupportedError } from "../core/errors.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { VIRTUAL_ROOT_NODE_ID, createNodeId } from "./ids.mjs";
|
|
2
|
+
import { createNodeId } from "./ids.mjs";
|
|
4
3
|
import { overlayBindEntry, overlayRenameEntry, overlayTombstoneEntry } from "./overlay.mjs";
|
|
5
|
-
import {
|
|
4
|
+
import { revertNodeState } from "./nodes.mjs";
|
|
6
5
|
import { modeToVirtualEntryKind, readRepoBlobContent } from "./origin.mjs";
|
|
7
|
-
import { assertValidVirtualPath,
|
|
8
|
-
import {
|
|
6
|
+
import { assertValidVirtualPath, normalizeDirectoryPath } from "./path.mjs";
|
|
7
|
+
import { getDirectoryChildrenView, getRootNode, requireExistingWriteTarget, requireMissingWriteTarget, resolveLeafWriteTarget, resolvePath, resolveWriteTransfer } from "./session-internal.mjs";
|
|
8
|
+
import { createChangeIndexPlanner } from "./change-index-plan.mjs";
|
|
9
|
+
import { computeVirtualDiff, rebuildNormalizedChangeIndex, refreshChangeRecordForPath, replaceChangeRecords, rewriteChangeRecordForRename, writeChangeRecordForCopy } from "./change-index.mjs";
|
|
10
|
+
import { createDirtyDirPlanner } from "./dirty-dir-plan.mjs";
|
|
11
|
+
import { cloneNodeGraphForCopy, runInWriteTransaction, statDirectoryNode, statNode, updateParentOverlay } from "./session-transaction.mjs";
|
|
9
12
|
import { writeTreeFromSession } from "./write-tree.mjs";
|
|
10
13
|
import { createVirtualWorkdirMemoryStateStore } from "./memory-backend.mjs";
|
|
11
14
|
//#region src/workdir/session.ts
|
|
@@ -39,6 +42,54 @@ function createVirtualWorkdirSession(source, options) {
|
|
|
39
42
|
* ```
|
|
40
43
|
*/
|
|
41
44
|
function openVirtualWorkdirSession(source, state) {
|
|
45
|
+
const currentNodeHashes = /* @__PURE__ */ new Map();
|
|
46
|
+
const invalidateDiffCaches = () => {
|
|
47
|
+
currentNodeHashes.clear();
|
|
48
|
+
};
|
|
49
|
+
const refreshChangeIndex = () => {
|
|
50
|
+
invalidateDiffCaches();
|
|
51
|
+
replaceChangeRecords(state, rebuildNormalizedChangeIndex(source, state, {
|
|
52
|
+
currentNodeHashes,
|
|
53
|
+
setCurrentNodeHash(nodeId, hash) {
|
|
54
|
+
currentNodeHashes.set(nodeId, hash);
|
|
55
|
+
}
|
|
56
|
+
}));
|
|
57
|
+
};
|
|
58
|
+
const refreshChangeIndexForPath = (path) => {
|
|
59
|
+
invalidateDiffCaches();
|
|
60
|
+
refreshChangeRecordForPath(source, state, path, {
|
|
61
|
+
currentNodeHashes,
|
|
62
|
+
setCurrentNodeHash(nodeId, hash) {
|
|
63
|
+
currentNodeHashes.set(nodeId, hash);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
const rewriteChangeIndexForRename = (from, to) => {
|
|
68
|
+
invalidateDiffCaches();
|
|
69
|
+
rewriteChangeRecordForRename(source, state, from, to, {
|
|
70
|
+
currentNodeHashes,
|
|
71
|
+
setCurrentNodeHash(nodeId, hash) {
|
|
72
|
+
currentNodeHashes.set(nodeId, hash);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
const writeChangeIndexForCopy = (from, to) => {
|
|
77
|
+
invalidateDiffCaches();
|
|
78
|
+
writeChangeRecordForCopy(source, state, from, to, {
|
|
79
|
+
currentNodeHashes,
|
|
80
|
+
setCurrentNodeHash(nodeId, hash) {
|
|
81
|
+
currentNodeHashes.set(nodeId, hash);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
const changeIndexPlanner = createChangeIndexPlanner(source, state, {
|
|
86
|
+
rebuildAll: refreshChangeIndex,
|
|
87
|
+
refreshPath: refreshChangeIndexForPath,
|
|
88
|
+
rewriteRename: rewriteChangeIndexForRename,
|
|
89
|
+
writeCopy: writeChangeIndexForCopy
|
|
90
|
+
});
|
|
91
|
+
const dirtyDirPlanner = createDirtyDirPlanner(source, state);
|
|
92
|
+
refreshChangeIndex();
|
|
42
93
|
return {
|
|
43
94
|
get baseTree() {
|
|
44
95
|
return state.readBaseTree();
|
|
@@ -61,7 +112,7 @@ function openVirtualWorkdirSession(source, state) {
|
|
|
61
112
|
} : resolvePath(source, state, normalized);
|
|
62
113
|
if (!resolved.found || resolved.node === null) throw new VirtualPathNotFoundError(normalized);
|
|
63
114
|
if (resolved.node.state.kind !== "directory") throw new VirtualNotDirectoryError(normalized);
|
|
64
|
-
return
|
|
115
|
+
return getDirectoryChildrenView(source, state, resolved.node, normalized).children.map((child) => ({
|
|
65
116
|
name: child.name,
|
|
66
117
|
kind: modeToVirtualEntryKind(child.mode),
|
|
67
118
|
mode: child.mode
|
|
@@ -91,19 +142,8 @@ function openVirtualWorkdirSession(source, state) {
|
|
|
91
142
|
throw new VirtualPathNotFoundError(path);
|
|
92
143
|
},
|
|
93
144
|
mkdir(path) {
|
|
94
|
-
runInWriteTransaction(state, () => {
|
|
95
|
-
|
|
96
|
-
const parent = parentPath(path);
|
|
97
|
-
const name = baseName(path);
|
|
98
|
-
const parentResolved = parent !== null ? resolvePath(source, state, parent) : {
|
|
99
|
-
found: true,
|
|
100
|
-
node: getRootNode(state)
|
|
101
|
-
};
|
|
102
|
-
if (!parentResolved.found || parentResolved.node === null) throw new VirtualPathNotFoundError(parent ?? path);
|
|
103
|
-
const parentNode = parentResolved.node;
|
|
104
|
-
if (parentNode.state.kind !== "directory") throw new VirtualNotDirectoryError(parent ?? path);
|
|
105
|
-
const existing = resolveChild(source, state, parentNode, parent ?? "", name);
|
|
106
|
-
if (existing.found && existing.node !== null) throw new VirtualPathAlreadyExistsError(path);
|
|
145
|
+
runInWriteTransaction(state, () => dirtyDirPlanner.rebuild([path]), invalidateDiffCaches, () => {
|
|
146
|
+
const target = requireMissingWriteTarget(source, state, path);
|
|
107
147
|
const nodeId = createNodeId();
|
|
108
148
|
const newNode = {
|
|
109
149
|
id: nodeId,
|
|
@@ -117,35 +157,20 @@ function openVirtualWorkdirSession(source, state) {
|
|
|
117
157
|
}
|
|
118
158
|
};
|
|
119
159
|
state.setNode(newNode);
|
|
120
|
-
updateParentOverlay(state, parentNode.id, overlayBindEntry(parentNode.state.overlay, name, nodeId));
|
|
121
|
-
state.appendChange({
|
|
122
|
-
op: "add",
|
|
123
|
-
path
|
|
124
|
-
});
|
|
160
|
+
updateParentOverlay(state, target.parentNode.id, overlayBindEntry(target.parentNode.state.overlay, target.name, nodeId));
|
|
125
161
|
});
|
|
126
162
|
},
|
|
127
163
|
writeFile(path, content, options) {
|
|
128
164
|
runInWriteTransaction(state, () => {
|
|
129
|
-
|
|
165
|
+
changeIndexPlanner.apply(changeIndexPlanner.planRefreshForPath(path));
|
|
166
|
+
dirtyDirPlanner.rebuild([path]);
|
|
167
|
+
}, invalidateDiffCaches, () => {
|
|
130
168
|
const mode = options?.mode ?? "100644";
|
|
131
|
-
const
|
|
132
|
-
const
|
|
133
|
-
const parentResolved = parent !== null ? resolvePath(source, state, parent) : {
|
|
134
|
-
found: true,
|
|
135
|
-
node: getRootNode(state)
|
|
136
|
-
};
|
|
137
|
-
if (!parentResolved.found || parentResolved.node === null) throw new VirtualPathNotFoundError(parent ?? path);
|
|
138
|
-
const parentNode = parentResolved.node;
|
|
139
|
-
if (parentNode.state.kind !== "directory") throw new VirtualNotDirectoryError(parent ?? path);
|
|
140
|
-
const existing = resolveChild(source, state, parentNode, parent ?? "", name);
|
|
141
|
-
if (existing.found && existing.node !== null) {
|
|
142
|
-
if (existing.node.state.kind === "directory") throw new VirtualNotFileError(path);
|
|
143
|
-
}
|
|
144
|
-
const isNew = !existing.found || existing.node === null;
|
|
145
|
-
const nodeId = existing.found ? existing.node.id : createNodeId();
|
|
169
|
+
const target = resolveLeafWriteTarget(source, state, path);
|
|
170
|
+
const nodeId = target.existing !== null ? target.existing.node.id : createNodeId();
|
|
146
171
|
const fileNode = {
|
|
147
172
|
id: nodeId,
|
|
148
|
-
origin: existing
|
|
173
|
+
origin: target.existing !== null ? target.existing.node.origin : { kind: "none" },
|
|
149
174
|
state: {
|
|
150
175
|
kind: "file",
|
|
151
176
|
mode,
|
|
@@ -153,34 +178,19 @@ function openVirtualWorkdirSession(source, state) {
|
|
|
153
178
|
}
|
|
154
179
|
};
|
|
155
180
|
state.setNode(fileNode);
|
|
156
|
-
updateParentOverlay(state, parentNode.id, overlayBindEntry(parentNode.state.overlay, name, nodeId));
|
|
157
|
-
state.appendChange({
|
|
158
|
-
op: isNew ? "add" : "modify",
|
|
159
|
-
path
|
|
160
|
-
});
|
|
181
|
+
if (target.existing === null || target.parentNode.state.overlay.addedEntries.has(target.name)) updateParentOverlay(state, target.parentNode.id, overlayBindEntry(target.parentNode.state.overlay, target.name, nodeId));
|
|
161
182
|
});
|
|
162
183
|
},
|
|
163
184
|
writeLink(path, target) {
|
|
164
185
|
runInWriteTransaction(state, () => {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
node: getRootNode(state)
|
|
171
|
-
};
|
|
172
|
-
if (!parentResolved.found || parentResolved.node === null) throw new VirtualPathNotFoundError(parent ?? path);
|
|
173
|
-
const parentNode = parentResolved.node;
|
|
174
|
-
if (parentNode.state.kind !== "directory") throw new VirtualNotDirectoryError(parent ?? path);
|
|
175
|
-
const existing = resolveChild(source, state, parentNode, parent ?? "", name);
|
|
176
|
-
if (existing.found && existing.node !== null) {
|
|
177
|
-
if (existing.node.state.kind === "directory") throw new VirtualNotFileError(path);
|
|
178
|
-
}
|
|
179
|
-
const isNew = !existing.found || existing.node === null;
|
|
180
|
-
const nodeId = existing.found ? existing.node.id : createNodeId();
|
|
186
|
+
changeIndexPlanner.apply(changeIndexPlanner.planRefreshForPath(path));
|
|
187
|
+
dirtyDirPlanner.rebuild([path]);
|
|
188
|
+
}, invalidateDiffCaches, () => {
|
|
189
|
+
const writeTarget = resolveLeafWriteTarget(source, state, path);
|
|
190
|
+
const nodeId = writeTarget.existing !== null ? writeTarget.existing.node.id : createNodeId();
|
|
181
191
|
const linkNode = {
|
|
182
192
|
id: nodeId,
|
|
183
|
-
origin: existing
|
|
193
|
+
origin: writeTarget.existing !== null ? writeTarget.existing.node.origin : { kind: "none" },
|
|
184
194
|
state: {
|
|
185
195
|
kind: "symlink",
|
|
186
196
|
mode: "120000",
|
|
@@ -188,118 +198,61 @@ function openVirtualWorkdirSession(source, state) {
|
|
|
188
198
|
}
|
|
189
199
|
};
|
|
190
200
|
state.setNode(linkNode);
|
|
191
|
-
updateParentOverlay(state, parentNode.id, overlayBindEntry(parentNode.state.overlay, name, nodeId));
|
|
192
|
-
state.appendChange({
|
|
193
|
-
op: isNew ? "add" : "modify",
|
|
194
|
-
path
|
|
195
|
-
});
|
|
201
|
+
if (writeTarget.existing === null || writeTarget.parentNode.state.overlay.addedEntries.has(writeTarget.name)) updateParentOverlay(state, writeTarget.parentNode.id, overlayBindEntry(writeTarget.parentNode.state.overlay, writeTarget.name, nodeId));
|
|
196
202
|
});
|
|
197
203
|
},
|
|
198
204
|
delete(path) {
|
|
199
205
|
runInWriteTransaction(state, () => {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
node: getRootNode(state)
|
|
206
|
-
};
|
|
207
|
-
if (!parentResolved.found || parentResolved.node === null) throw new VirtualPathNotFoundError(parent ?? path);
|
|
208
|
-
const parentNode = parentResolved.node;
|
|
209
|
-
if (parentNode.state.kind !== "directory") throw new VirtualNotDirectoryError(parent ?? path);
|
|
210
|
-
const existing = resolveChild(source, state, parentNode, parent ?? "", name);
|
|
211
|
-
if (!existing.found || existing.node === null) throw new VirtualPathNotFoundError(path);
|
|
212
|
-
updateParentOverlay(state, parentNode.id, overlayTombstoneEntry(parentNode.state.overlay, name));
|
|
213
|
-
state.appendChange({
|
|
214
|
-
op: "delete",
|
|
215
|
-
path
|
|
216
|
-
});
|
|
206
|
+
changeIndexPlanner.apply(changeIndexPlanner.planRefreshForPath(path, { treatMissingAsIncremental: true }));
|
|
207
|
+
dirtyDirPlanner.rebuild([path]);
|
|
208
|
+
}, invalidateDiffCaches, () => {
|
|
209
|
+
const target = requireExistingWriteTarget(source, state, path);
|
|
210
|
+
updateParentOverlay(state, target.parentNode.id, overlayTombstoneEntry(target.parentNode.state.overlay, target.name));
|
|
217
211
|
});
|
|
218
212
|
},
|
|
219
213
|
rename(from, to) {
|
|
220
214
|
runInWriteTransaction(state, () => {
|
|
215
|
+
changeIndexPlanner.apply(changeIndexPlanner.planRewriteForRename(from, to));
|
|
216
|
+
dirtyDirPlanner.rebuild([from, to]);
|
|
217
|
+
}, invalidateDiffCaches, () => {
|
|
221
218
|
assertValidVirtualPath(from);
|
|
222
219
|
assertValidVirtualPath(to);
|
|
223
220
|
if (from === to) return;
|
|
224
|
-
const
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
found: true,
|
|
228
|
-
node: getRootNode(state)
|
|
229
|
-
};
|
|
230
|
-
if (!fromParentResolved.found || fromParentResolved.node === null) throw new VirtualPathNotFoundError(from);
|
|
231
|
-
const fromParentNode = fromParentResolved.node;
|
|
232
|
-
if (fromParentNode.state.kind !== "directory") throw new VirtualNotDirectoryError(from);
|
|
233
|
-
const fromChild = resolveChild(source, state, fromParentNode, fromParent ?? "", fromName);
|
|
234
|
-
if (!fromChild.found || fromChild.node === null) throw new VirtualPathNotFoundError(from);
|
|
235
|
-
const sourceNode = fromChild.node;
|
|
236
|
-
const toParent = parentPath(to);
|
|
237
|
-
const toName = baseName(to);
|
|
238
|
-
const toParentResolved = toParent !== null ? resolvePath(source, state, toParent) : {
|
|
239
|
-
found: true,
|
|
240
|
-
node: getRootNode(state)
|
|
241
|
-
};
|
|
242
|
-
if (!toParentResolved.found || toParentResolved.node === null) throw new VirtualPathNotFoundError(to);
|
|
243
|
-
const toParentNode = toParentResolved.node;
|
|
244
|
-
if (toParentNode.state.kind !== "directory") throw new VirtualNotDirectoryError(to);
|
|
245
|
-
const toExisting = resolveChild(source, state, toParentNode, toParent ?? "", toName);
|
|
246
|
-
if (toExisting.found && toExisting.node !== null) throw new VirtualPathAlreadyExistsError(to);
|
|
221
|
+
const { from: fromTarget, to: toTarget } = resolveWriteTransfer(source, state, from, to);
|
|
222
|
+
const sourceNode = fromTarget.existing.node;
|
|
223
|
+
if (toTarget.existing !== null) throw new VirtualPathAlreadyExistsError(to);
|
|
247
224
|
if (sourceNode.state.kind === "directory") {
|
|
248
225
|
const toPath = to;
|
|
249
226
|
const fromPath = from;
|
|
250
227
|
if (toPath.startsWith(fromPath + "/") || toPath === fromPath) throw new Error(`Cannot rename '${from}' to '${to}': destination is a subdirectory of source`);
|
|
251
228
|
}
|
|
252
|
-
if (
|
|
229
|
+
if (fromTarget.parentNode.id === toTarget.parentNode.id) updateParentOverlay(state, fromTarget.parentNode.id, overlayRenameEntry(fromTarget.parentNode.state.overlay, fromTarget.name, toTarget.name, sourceNode.id));
|
|
253
230
|
else {
|
|
254
|
-
updateParentOverlay(state,
|
|
255
|
-
updateParentOverlay(state,
|
|
231
|
+
updateParentOverlay(state, fromTarget.parentNode.id, overlayTombstoneEntry(fromTarget.parentNode.state.overlay, fromTarget.name));
|
|
232
|
+
updateParentOverlay(state, toTarget.parentNode.id, overlayBindEntry(toTarget.parentNode.state.overlay, toTarget.name, sourceNode.id));
|
|
256
233
|
}
|
|
257
|
-
state.appendChange({
|
|
258
|
-
op: "rename",
|
|
259
|
-
from,
|
|
260
|
-
to
|
|
261
|
-
});
|
|
262
234
|
});
|
|
263
235
|
},
|
|
264
236
|
copy(from, to) {
|
|
265
237
|
runInWriteTransaction(state, () => {
|
|
238
|
+
changeIndexPlanner.apply(changeIndexPlanner.planWriteForCopy(from, to));
|
|
239
|
+
dirtyDirPlanner.rebuild([from, to]);
|
|
240
|
+
}, invalidateDiffCaches, () => {
|
|
266
241
|
assertValidVirtualPath(from);
|
|
267
242
|
assertValidVirtualPath(to);
|
|
268
243
|
if (from === to) throw new VirtualPathAlreadyExistsError(to);
|
|
269
|
-
const
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
found: true,
|
|
273
|
-
node: getRootNode(state)
|
|
274
|
-
};
|
|
275
|
-
if (!fromParentResolved.found || fromParentResolved.node === null) throw new VirtualPathNotFoundError(from);
|
|
276
|
-
const fromParentNode = fromParentResolved.node;
|
|
277
|
-
if (fromParentNode.state.kind !== "directory") throw new VirtualNotDirectoryError(from);
|
|
278
|
-
const fromChild = resolveChild(source, state, fromParentNode, fromParent ?? "", fromName);
|
|
279
|
-
if (!fromChild.found || fromChild.node === null) throw new VirtualPathNotFoundError(from);
|
|
280
|
-
const sourceNode = fromChild.node;
|
|
281
|
-
const toParent = parentPath(to);
|
|
282
|
-
const toName = baseName(to);
|
|
283
|
-
const toParentResolved = toParent !== null ? resolvePath(source, state, toParent) : {
|
|
284
|
-
found: true,
|
|
285
|
-
node: getRootNode(state)
|
|
286
|
-
};
|
|
287
|
-
if (!toParentResolved.found || toParentResolved.node === null) throw new VirtualPathNotFoundError(to);
|
|
288
|
-
const toParentNode = toParentResolved.node;
|
|
289
|
-
if (toParentNode.state.kind !== "directory") throw new VirtualNotDirectoryError(to);
|
|
290
|
-
const toExisting = resolveChild(source, state, toParentNode, toParent ?? "", toName);
|
|
291
|
-
if (toExisting.found && toExisting.node !== null) throw new VirtualPathAlreadyExistsError(to);
|
|
244
|
+
const { from: fromTarget, to: toTarget } = resolveWriteTransfer(source, state, from, to);
|
|
245
|
+
const sourceNode = fromTarget.existing.node;
|
|
246
|
+
if (toTarget.existing !== null) throw new VirtualPathAlreadyExistsError(to);
|
|
292
247
|
const newNodeId = cloneNodeGraphForCopy(source, state, sourceNode, from);
|
|
293
|
-
updateParentOverlay(state,
|
|
294
|
-
state.appendChange({
|
|
295
|
-
op: "copy",
|
|
296
|
-
from,
|
|
297
|
-
to
|
|
298
|
-
});
|
|
248
|
+
updateParentOverlay(state, toTarget.parentNode.id, overlayBindEntry(toTarget.parentNode.state.overlay, toTarget.name, newNodeId));
|
|
299
249
|
});
|
|
300
250
|
},
|
|
301
251
|
revert(path) {
|
|
302
252
|
runInWriteTransaction(state, () => {
|
|
253
|
+
changeIndexPlanner.apply(changeIndexPlanner.planRefreshForPath(path));
|
|
254
|
+
dirtyDirPlanner.rebuild([path]);
|
|
255
|
+
}, invalidateDiffCaches, () => {
|
|
303
256
|
assertValidVirtualPath(path);
|
|
304
257
|
const resolved = resolvePath(source, state, path);
|
|
305
258
|
if (!resolved.found || resolved.node === null) throw new VirtualPathNotFoundError(path);
|
|
@@ -307,101 +260,21 @@ function openVirtualWorkdirSession(source, state) {
|
|
|
307
260
|
const reverted = revertNodeState(node);
|
|
308
261
|
if (reverted === node) throw new VirtualRevertNotSupportedError(path);
|
|
309
262
|
state.setNode(reverted);
|
|
310
|
-
state.appendChange({
|
|
311
|
-
op: "revert",
|
|
312
|
-
path
|
|
313
|
-
});
|
|
314
263
|
});
|
|
315
264
|
},
|
|
265
|
+
diff() {
|
|
266
|
+
return computeVirtualDiff(state);
|
|
267
|
+
},
|
|
316
268
|
writeTree() {
|
|
317
269
|
return writeTreeFromSession(source, state);
|
|
318
270
|
},
|
|
319
271
|
reset(baseTree) {
|
|
320
|
-
runInWriteTransaction(state, () => {
|
|
272
|
+
runInWriteTransaction(state, null, invalidateDiffCaches, () => {
|
|
321
273
|
state.reset(baseTree);
|
|
274
|
+
dirtyDirPlanner.clear();
|
|
322
275
|
});
|
|
323
|
-
},
|
|
324
|
-
listChanges() {
|
|
325
|
-
return mapInternalChangesToVirtualChanges(state.listChangeRecords());
|
|
326
|
-
}
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
function getRootNode(state) {
|
|
330
|
-
const root = state.getNode(VIRTUAL_ROOT_NODE_ID);
|
|
331
|
-
if (root === null) throw new Error("Virtual workdir session is missing root node");
|
|
332
|
-
return root;
|
|
333
|
-
}
|
|
334
|
-
function runInWriteTransaction(state, fn) {
|
|
335
|
-
return state.transact(fn);
|
|
336
|
-
}
|
|
337
|
-
/**
|
|
338
|
-
* 更新父节点的 overlay(创建新节点对象替代 Map 中的旧引用)
|
|
339
|
-
*/
|
|
340
|
-
function updateParentOverlay(state, parentId, newOverlay) {
|
|
341
|
-
const parentNode = state.getNode(parentId);
|
|
342
|
-
if (parentNode === null || parentNode.state.kind !== "directory") throw new Error("updateParentOverlay: parent is not a directory");
|
|
343
|
-
state.setNode({
|
|
344
|
-
...parentNode,
|
|
345
|
-
state: {
|
|
346
|
-
...parentNode.state,
|
|
347
|
-
overlay: newOverlay
|
|
348
276
|
}
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
function statNode(source, node, path) {
|
|
352
|
-
if (node.state.kind === "directory") return statDirectoryNode(node);
|
|
353
|
-
if (node.state.kind === "symlink") {
|
|
354
|
-
const hash = node.origin.kind === "repo-blob" ? node.origin.hash : null;
|
|
355
|
-
let size = 0;
|
|
356
|
-
if (node.state.target !== void 0) size = node.state.target.length;
|
|
357
|
-
else if (hash !== null) size = readRepoBlobContent(source, hash, path).length;
|
|
358
|
-
return {
|
|
359
|
-
kind: "symlink",
|
|
360
|
-
mode: "120000",
|
|
361
|
-
size,
|
|
362
|
-
hash
|
|
363
|
-
};
|
|
364
|
-
}
|
|
365
|
-
const hash = node.origin.kind === "repo-blob" ? node.origin.hash : null;
|
|
366
|
-
let size = 0;
|
|
367
|
-
if (node.state.content !== void 0) size = node.state.content.length;
|
|
368
|
-
else if (hash !== null) size = readRepoBlobContent(source, hash, path).length;
|
|
369
|
-
return {
|
|
370
|
-
kind: "blob",
|
|
371
|
-
mode: node.state.mode,
|
|
372
|
-
size,
|
|
373
|
-
hash
|
|
374
277
|
};
|
|
375
278
|
}
|
|
376
|
-
function statDirectoryNode(node) {
|
|
377
|
-
return {
|
|
378
|
-
kind: "tree",
|
|
379
|
-
mode: "40000",
|
|
380
|
-
size: 0,
|
|
381
|
-
hash: node.origin.kind === "repo-tree" ? node.origin.hash : null
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
function cloneNodeGraphForCopy(source, state, node, path) {
|
|
385
|
-
const newNodeId = createNodeId();
|
|
386
|
-
const cloned = cloneSessionNodeForCopy(node, newNodeId);
|
|
387
|
-
state.setNode(cloned);
|
|
388
|
-
if (node.state.kind !== "directory" || cloned.state.kind !== "directory") return newNodeId;
|
|
389
|
-
let overlay = cloned.state.overlay;
|
|
390
|
-
const children = listDirectoryChildren(source, state, node, path);
|
|
391
|
-
for (const child of children) {
|
|
392
|
-
const childNode = state.getNode(child.nodeId);
|
|
393
|
-
if (childNode === null) continue;
|
|
394
|
-
const clonedChildId = cloneNodeGraphForCopy(source, state, childNode, path === "" ? child.name : `${path}/${child.name}`);
|
|
395
|
-
overlay = overlayBindEntry(overlay, child.name, clonedChildId);
|
|
396
|
-
}
|
|
397
|
-
state.setNode({
|
|
398
|
-
...cloned,
|
|
399
|
-
state: {
|
|
400
|
-
kind: "directory",
|
|
401
|
-
overlay
|
|
402
|
-
}
|
|
403
|
-
});
|
|
404
|
-
return newNodeId;
|
|
405
|
-
}
|
|
406
279
|
//#endregion
|
|
407
280
|
export { createVirtualWorkdirSession, openVirtualWorkdirSession };
|