pagan-artifact 0.2.8 → 0.3.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.
- package/bin/art.js +15 -2
- package/branching/index.js +117 -46
- package/caches/index.js +112 -68
- package/changes/index.js +49 -15
- package/contributions/index.js +125 -95
- package/index.js +2 -2
- package/package.json +1 -1
- package/setup/index.js +197 -157
- package/utils/getStateByHash/index.js +61 -41
- package/workflow/index.js +199 -150
package/setup/index.js
CHANGED
|
@@ -1,99 +1,133 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* art - Modern version control.
|
|
3
|
-
* Module: Setup (v0.
|
|
3
|
+
* Module: Setup (v0.3.0)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const path = require('path');
|
|
8
8
|
|
|
9
9
|
const pkg = require('../package.json');
|
|
10
|
-
const { remote } = require('../contributions');
|
|
11
10
|
const shouldIgnore = require('../utils/shouldIgnore');
|
|
12
11
|
|
|
13
12
|
const ARTIFACT_HOST = pkg.artConfig.host || 'http://localhost:1337';
|
|
14
13
|
|
|
15
14
|
/**
|
|
16
|
-
*
|
|
15
|
+
* Internal helper to create the .art directory tree.
|
|
17
16
|
*/
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (!fs.existsSync(fullPath)) {
|
|
40
|
-
fs.mkdirSync(fullPath, { recursive: true });
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const files = fs.readdirSync(directoryPath, { recursive: true })
|
|
45
|
-
.filter(f => {
|
|
46
|
-
const isInternal = f === '.art' || f.startsWith('.art' + path.sep);
|
|
47
|
-
|
|
48
|
-
return !isInternal && !shouldIgnore(f);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
const rootManifest = { files: [] };
|
|
52
|
-
|
|
53
|
-
for (const file of files) {
|
|
54
|
-
const fullPath = path.join(directoryPath, file);
|
|
55
|
-
|
|
56
|
-
if (fs.lstatSync(fullPath).isFile()) {
|
|
57
|
-
rootManifest.files.push({
|
|
58
|
-
path: file,
|
|
59
|
-
content: fs.readFileSync(fullPath, 'utf8')
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
fs.writeFileSync(
|
|
65
|
-
path.join(artDirectory, 'root/manifest.json'),
|
|
66
|
-
JSON.stringify(rootManifest, null, 2)
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
fs.writeFileSync(
|
|
70
|
-
path.join(artDirectory, 'history/local/main/manifest.json'),
|
|
71
|
-
JSON.stringify({ commits: [] }, null, 2)
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
fs.writeFileSync(
|
|
75
|
-
path.join(artDirectory, 'history/remote/main/manifest.json'),
|
|
76
|
-
JSON.stringify({ commits: [] }, null, 2)
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
const artFile = {
|
|
80
|
-
active: { branch: 'main', parent: null },
|
|
81
|
-
remote: '',
|
|
82
|
-
configuration: { handle: '', personalAccessToken: '' }
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
fs.writeFileSync(
|
|
86
|
-
path.join(artDirectory, 'art.json'),
|
|
87
|
-
JSON.stringify(artFile, null, 2)
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
return `Initialized empty art repository in ${artDirectory}`;
|
|
91
|
-
}
|
|
18
|
+
function ensureDirStructure(artDirectory) {
|
|
19
|
+
const folders = [
|
|
20
|
+
'',
|
|
21
|
+
'root',
|
|
22
|
+
'history',
|
|
23
|
+
'history/local',
|
|
24
|
+
'history/local/main',
|
|
25
|
+
'history/remote',
|
|
26
|
+
'history/remote/main'
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
for (const folder of folders) {
|
|
30
|
+
const fullPath = path.join(artDirectory, folder);
|
|
31
|
+
|
|
32
|
+
if (!fs.existsSync(fullPath)) {
|
|
33
|
+
fs.mkdirSync(fullPath, { recursive: true });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
92
37
|
|
|
93
38
|
/**
|
|
94
|
-
*
|
|
95
|
-
|
|
96
|
-
|
|
39
|
+
* Initializes the local .art directory structure and indexes current files.
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
function init (directoryPath = process.cwd()) {
|
|
43
|
+
const artDirectory = path.join(directoryPath, '.art');
|
|
44
|
+
|
|
45
|
+
if (fs.existsSync(artDirectory)) {
|
|
46
|
+
return `Reinitialized existing art repository in ${artDirectory}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
ensureDirStructure(artDirectory);
|
|
50
|
+
|
|
51
|
+
const files = fs.readdirSync(directoryPath, { recursive: true })
|
|
52
|
+
.filter(f => {
|
|
53
|
+
const isInternal = f === '.art' || f.startsWith('.art' + path.sep);
|
|
54
|
+
return !isInternal && !shouldIgnore(f);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const rootMasterManifest = { parts: [] };
|
|
58
|
+
let currentPartFiles = [];
|
|
59
|
+
let currentPartChars = 0;
|
|
60
|
+
const MAX_PART_CHARS = 32000000;
|
|
61
|
+
|
|
62
|
+
const saveManifestPart = () => {
|
|
63
|
+
if (currentPartFiles.length === 0) return;
|
|
64
|
+
|
|
65
|
+
const partIndex = rootMasterManifest.parts.length;
|
|
66
|
+
const partName = `manifest.part.${Date.now()}.${partIndex}.json`;
|
|
67
|
+
const partPath = path.join(artDirectory, 'root', partName);
|
|
68
|
+
|
|
69
|
+
fs.writeFileSync(
|
|
70
|
+
partPath,
|
|
71
|
+
JSON.stringify({ files: currentPartFiles }, null, 2)
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
rootMasterManifest.parts.push(partName);
|
|
75
|
+
currentPartFiles = [];
|
|
76
|
+
currentPartChars = 0;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
for (const file of files) {
|
|
80
|
+
const fullPath = path.join(directoryPath, file);
|
|
81
|
+
|
|
82
|
+
if (fs.lstatSync(fullPath).isFile()) {
|
|
83
|
+
const content = fs.readFileSync(fullPath, 'utf8');
|
|
84
|
+
|
|
85
|
+
if (currentPartChars + content.length > MAX_PART_CHARS && currentPartFiles.length > 0) {
|
|
86
|
+
saveManifestPart();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
currentPartFiles.push({
|
|
90
|
+
path: file,
|
|
91
|
+
content: content
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
currentPartChars += content.length;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
saveManifestPart();
|
|
99
|
+
|
|
100
|
+
fs.writeFileSync(
|
|
101
|
+
path.join(artDirectory, 'root/manifest.json'),
|
|
102
|
+
JSON.stringify(rootMasterManifest, null, 2)
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
fs.writeFileSync(
|
|
106
|
+
path.join(artDirectory, 'history/local/main/manifest.json'),
|
|
107
|
+
JSON.stringify({ commits: [] }, null, 2)
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
fs.writeFileSync(
|
|
111
|
+
path.join(artDirectory, 'history/remote/main/manifest.json'),
|
|
112
|
+
JSON.stringify({ commits: [] }, null, 2)
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
const artFile = {
|
|
116
|
+
active: { branch: 'main', parent: null },
|
|
117
|
+
remote: '',
|
|
118
|
+
configuration: { handle: '', personalAccessToken: '' }
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
fs.writeFileSync(
|
|
122
|
+
path.join(artDirectory, 'art.json'),
|
|
123
|
+
JSON.stringify(artFile, null, 2)
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
return `Initialized empty art repository in ${artDirectory}.`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Clone a repository and replay history.
|
|
97
131
|
*/
|
|
98
132
|
|
|
99
133
|
async function clone (repoSlug, providedToken = null) {
|
|
@@ -110,19 +144,20 @@ async function clone (repoSlug, providedToken = null) {
|
|
|
110
144
|
}
|
|
111
145
|
|
|
112
146
|
fs.mkdirSync(targetPath, { recursive: true });
|
|
113
|
-
|
|
147
|
+
|
|
148
|
+
const artPath = path.join(targetPath, '.art');
|
|
149
|
+
|
|
150
|
+
ensureDirStructure(artPath);
|
|
114
151
|
process.chdir(targetPath);
|
|
115
152
|
|
|
116
153
|
try {
|
|
117
|
-
const artPath = path.join(targetPath, '.art');
|
|
118
154
|
const artJsonPath = path.join(artPath, 'art.json');
|
|
119
|
-
const artJson = JSON.parse(fs.readFileSync(artJsonPath, 'utf8'));
|
|
120
155
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
156
|
+
const artJson = {
|
|
157
|
+
active: { branch: 'main', parent: null },
|
|
158
|
+
remote: `${ARTIFACT_HOST}/${handle}/${repo}`,
|
|
159
|
+
configuration: { handle: '', personalAccessToken: providedToken || '' }
|
|
160
|
+
};
|
|
126
161
|
|
|
127
162
|
fs.writeFileSync(artJsonPath, JSON.stringify(artJson, null, 2));
|
|
128
163
|
|
|
@@ -136,26 +171,26 @@ async function clone (repoSlug, providedToken = null) {
|
|
|
136
171
|
handle,
|
|
137
172
|
repo,
|
|
138
173
|
branch: 'main',
|
|
139
|
-
|
|
140
174
|
...(token && { personalAccessToken: token })
|
|
141
175
|
})
|
|
142
176
|
});
|
|
143
177
|
|
|
144
|
-
|
|
145
|
-
throw new Error(`Failed to fetch root: ${rootRes.statusText}`);
|
|
146
|
-
}
|
|
178
|
+
const masterManifest = await rootRes.json();
|
|
147
179
|
|
|
148
|
-
|
|
180
|
+
fs.writeFileSync(path.join(artPath, 'root/manifest.json'), JSON.stringify(masterManifest, null, 2));
|
|
149
181
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
182
|
+
for (const partName of masterManifest.parts) {
|
|
183
|
+
const partRes = await fetch(`${ARTIFACT_HOST}/part`, {
|
|
184
|
+
method: 'POST',
|
|
185
|
+
headers: { 'Content-Type': 'application/json' },
|
|
186
|
+
body: JSON.stringify({ handle, repo, partName, ...(token && { personalAccessToken: token }) })
|
|
187
|
+
});
|
|
155
188
|
|
|
156
|
-
|
|
157
|
-
|
|
189
|
+
const partData = await partRes.json();
|
|
190
|
+
fs.writeFileSync(path.join(artPath, 'root', partName), JSON.stringify(partData, null, 2));
|
|
158
191
|
|
|
192
|
+
for (const file of partData.files) {
|
|
193
|
+
const workingPath = path.join(targetPath, file.path);
|
|
159
194
|
fs.mkdirSync(path.dirname(workingPath), { recursive: true });
|
|
160
195
|
fs.writeFileSync(workingPath, file.content);
|
|
161
196
|
}
|
|
@@ -164,79 +199,85 @@ async function clone (repoSlug, providedToken = null) {
|
|
|
164
199
|
const historyRes = await fetch(`${ARTIFACT_HOST}/manifest`, {
|
|
165
200
|
method: 'POST',
|
|
166
201
|
headers: { 'Content-Type': 'application/json' },
|
|
167
|
-
body: JSON.stringify({
|
|
168
|
-
type: 'history',
|
|
169
|
-
handle,
|
|
170
|
-
repo,
|
|
171
|
-
branch: 'main',
|
|
172
|
-
|
|
173
|
-
...(token && { personalAccessToken: token })
|
|
174
|
-
})
|
|
202
|
+
body: JSON.stringify({ type: 'history', handle, repo, branch: 'main', ...(token && { personalAccessToken: token }) })
|
|
175
203
|
});
|
|
176
204
|
|
|
177
205
|
const historyManifest = await historyRes.json();
|
|
178
|
-
const localManifest = { commits: [] };
|
|
179
206
|
const localHistoryDir = path.join(artPath, 'history/local/main');
|
|
180
207
|
const remoteHistoryDir = path.join(artPath, 'history/remote/main');
|
|
181
208
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
...(token && { personalAccessToken: token })
|
|
209
|
+
for (const commitHash of historyManifest.commits) {
|
|
210
|
+
const commitRes = await fetch(`${ARTIFACT_HOST}/commit`, {
|
|
211
|
+
method: 'POST',
|
|
212
|
+
headers: { 'Content-Type': 'application/json' },
|
|
213
|
+
body: JSON.stringify({
|
|
214
|
+
handle,
|
|
215
|
+
repo,
|
|
216
|
+
branch: 'main',
|
|
217
|
+
hash: commitHash,
|
|
218
|
+
|
|
219
|
+
...(token && { personalAccessToken: token
|
|
194
220
|
})
|
|
195
|
-
})
|
|
221
|
+
})
|
|
222
|
+
});
|
|
196
223
|
|
|
197
|
-
|
|
224
|
+
const commitMaster = await commitRes.json();
|
|
198
225
|
|
|
199
|
-
|
|
200
|
-
const fullPath = path.join(targetPath, filePath);
|
|
201
|
-
const changeSet = commitDiff.changes[filePath];
|
|
226
|
+
let fullChanges = {};
|
|
202
227
|
|
|
203
|
-
|
|
204
|
-
|
|
228
|
+
if (commitMaster.parts && commitMaster.parts.length > 0) {
|
|
229
|
+
for (const partName of commitMaster.parts) {
|
|
230
|
+
const partRes = await fetch(`${ARTIFACT_HOST}/commit/part`, {
|
|
231
|
+
method: 'POST',
|
|
232
|
+
headers: { 'Content-Type': 'application/json' },
|
|
233
|
+
body: JSON.stringify({ handle, repo, branch: 'main', partName, ...(token && { personalAccessToken: token }) })
|
|
234
|
+
});
|
|
205
235
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
} else if (operation.type === 'delete') {
|
|
210
|
-
content = `${content.slice(0, operation.position)}${content.slice(operation.position + operation.length)}`;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
236
|
+
const partData = await partRes.json();
|
|
237
|
+
fs.writeFileSync(path.join(localHistoryDir, partName), JSON.stringify(partData, null, 2));
|
|
238
|
+
fs.writeFileSync(path.join(remoteHistoryDir, partName), JSON.stringify(partData, null, 2));
|
|
213
239
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
240
|
+
Object.assign(fullChanges, partData.changes);
|
|
241
|
+
}
|
|
242
|
+
} else if (commitMaster.changes) {
|
|
243
|
+
fullChanges = commitMaster.changes;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const masterContent = JSON.stringify(commitMaster, null, 2);
|
|
222
247
|
|
|
223
|
-
|
|
248
|
+
fs.writeFileSync(path.join(localHistoryDir, `${commitHash}.json`), masterContent);
|
|
249
|
+
fs.writeFileSync(path.join(remoteHistoryDir, `${commitHash}.json`), masterContent);
|
|
224
250
|
|
|
225
|
-
|
|
226
|
-
|
|
251
|
+
for (const [filePath, changeSet] of Object.entries(fullChanges)) {
|
|
252
|
+
const fullPath = path.join(targetPath, filePath);
|
|
227
253
|
|
|
228
|
-
|
|
229
|
-
|
|
254
|
+
if (Array.isArray(changeSet)) {
|
|
255
|
+
let content = fs.existsSync(fullPath) ? fs.readFileSync(fullPath, 'utf8') : '';
|
|
256
|
+
for (const op of changeSet) {
|
|
257
|
+
if (op.type === 'insert') {
|
|
258
|
+
content = content.slice(0, op.position) + op.content + content.slice(op.position);
|
|
259
|
+
} else if (op.type === 'delete') {
|
|
260
|
+
content = content.slice(0, op.position) + content.slice(op.position + op.length);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
fs.writeFileSync(fullPath, content);
|
|
264
|
+
} else if (changeSet.type === 'createFile') {
|
|
265
|
+
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
266
|
+
fs.writeFileSync(fullPath, changeSet.content || '');
|
|
267
|
+
} else if (changeSet.type === 'deleteFile' && fs.existsSync(fullPath)) {
|
|
268
|
+
fs.unlinkSync(fullPath);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
230
271
|
}
|
|
231
272
|
|
|
232
|
-
|
|
233
|
-
fs.writeFileSync(path.join(remoteHistoryDir, 'manifest.json'), JSON.stringify(localManifest, null, 2));
|
|
273
|
+
const manifestJson = JSON.stringify({ commits: historyManifest.commits }, null, 2);
|
|
234
274
|
|
|
235
|
-
|
|
275
|
+
fs.writeFileSync(path.join(localHistoryDir, 'manifest.json'), manifestJson);
|
|
276
|
+
fs.writeFileSync(path.join(remoteHistoryDir, 'manifest.json'), manifestJson);
|
|
236
277
|
|
|
237
|
-
if (
|
|
238
|
-
|
|
239
|
-
fs.writeFileSync(artJsonPath, JSON.stringify(
|
|
278
|
+
if (historyManifest.commits.length > 0) {
|
|
279
|
+
artJson.active.parent = historyManifest.commits[historyManifest.commits.length - 1];
|
|
280
|
+
fs.writeFileSync(artJsonPath, JSON.stringify(artJson, null, 2));
|
|
240
281
|
}
|
|
241
282
|
|
|
242
283
|
return `Successfully cloned and replayed ${repoSlug}.`;
|
|
@@ -250,8 +291,7 @@ async function clone (repoSlug, providedToken = null) {
|
|
|
250
291
|
/**
|
|
251
292
|
* Updates the configuration in art.json.
|
|
252
293
|
*/
|
|
253
|
-
|
|
254
|
-
function config (key, value) {
|
|
294
|
+
function config(key, value) {
|
|
255
295
|
const manifestPath = path.join(process.cwd(), '.art', 'art.json');
|
|
256
296
|
|
|
257
297
|
if (!fs.existsSync(manifestPath)) {
|
|
@@ -262,7 +302,6 @@ function config (key, value) {
|
|
|
262
302
|
|
|
263
303
|
if (key && value !== undefined) {
|
|
264
304
|
manifest.configuration[key] = value;
|
|
265
|
-
|
|
266
305
|
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
267
306
|
}
|
|
268
307
|
|
|
@@ -274,5 +313,6 @@ module.exports = {
|
|
|
274
313
|
__libraryAPIName: 'Setup',
|
|
275
314
|
init,
|
|
276
315
|
clone,
|
|
277
|
-
config
|
|
316
|
+
config,
|
|
317
|
+
ensureDirStructure
|
|
278
318
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* art - Modern version control.
|
|
3
|
-
* Module: Utils (v0.
|
|
3
|
+
* Module: Utils (v0.3.0)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const fs = require('fs');
|
|
@@ -10,58 +10,78 @@ const path = require('path');
|
|
|
10
10
|
* Helper to reconstruct file states at a specific commit hash.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
module.exports = (branchName, targetHash) => {
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
module.exports = (branchName, targetHash) => {
|
|
14
|
+
const artPath = path.join(process.cwd(), '.art');
|
|
15
|
+
const rootPath = path.join(artPath, 'root/manifest.json');
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
if (!fs.existsSync(rootPath)) return {};
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
const rootMaster = JSON.parse(fs.readFileSync(rootPath, 'utf8'));
|
|
20
|
+
const branchPath = path.join(artPath, 'history/local', branchName);
|
|
21
|
+
const manifestPath = path.join(branchPath, 'manifest.json');
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
if (!fs.existsSync(manifestPath)) return {};
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
let state = {};
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
for (const partName of rootMaster.parts) {
|
|
30
|
+
const partPath = path.join(artPath, 'root', partName);
|
|
31
|
+
const partData = JSON.parse(fs.readFileSync(partPath, 'utf8'));
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
for (const file of partData.files) {
|
|
34
|
+
state[file.path] = file.content;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
if (!targetHash) return state;
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
for (const hash of manifest.commits) {
|
|
41
|
+
const commitPath = path.join(branchPath, `${hash}.json`);
|
|
39
42
|
|
|
40
|
-
|
|
41
|
-
if (Array.isArray(changeSet)) {
|
|
42
|
-
let currentContent = state[filePath] || '';
|
|
43
|
+
if (!fs.existsSync(commitPath)) continue;
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
if (operation.type === 'insert') {
|
|
46
|
-
currentContent = `${currentContent.slice(0, operation.position)}${operation.content}${currentContent.slice(operation.position)}`;
|
|
47
|
-
} else if (operation.type === 'delete') {
|
|
48
|
-
currentContent = `${currentContent.slice(0, operation.position)}${currentContent.slice(operation.position + operation.length)}`;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
45
|
+
const commitMaster = JSON.parse(fs.readFileSync(commitPath, 'utf8'));
|
|
51
46
|
|
|
52
|
-
|
|
47
|
+
let fullChanges = {};
|
|
53
48
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
49
|
+
if (commitMaster.parts && Array.isArray(commitMaster.parts)) {
|
|
50
|
+
for (const partName of commitMaster.parts) {
|
|
51
|
+
const partPath = path.join(branchPath, partName);
|
|
52
|
+
if (fs.existsSync(partPath)) {
|
|
53
|
+
const partData = JSON.parse(fs.readFileSync(partPath, 'utf8'));
|
|
54
|
+
Object.assign(fullChanges, partData.changes);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
} else if (commitMaster.changes) {
|
|
58
|
+
fullChanges = commitMaster.changes;
|
|
59
|
+
}
|
|
62
60
|
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
for (const [filePath, changeSet] of Object.entries(fullChanges)) {
|
|
62
|
+
if (Array.isArray(changeSet)) {
|
|
63
|
+
let currentContent = state[filePath] || '';
|
|
65
64
|
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
for (const operation of changeSet) {
|
|
66
|
+
if (operation.type === 'insert') {
|
|
67
|
+
currentContent = `${currentContent.slice(0, operation.position)}${operation.content}${currentContent.slice(operation.position)}`;
|
|
68
|
+
} else if (operation.type === 'delete') {
|
|
69
|
+
currentContent = `${currentContent.slice(0, operation.position)}${currentContent.slice(operation.position + operation.length)}`;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
state[filePath] = currentContent;
|
|
74
|
+
} else {
|
|
75
|
+
if (changeSet.type === 'createFile') {
|
|
76
|
+
state[filePath] = changeSet.content;
|
|
77
|
+
} else if (changeSet.type === 'deleteFile') {
|
|
78
|
+
delete state[filePath];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (hash === targetHash) break;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return state;
|
|
87
|
+
};
|