pagan-artifact 0.3.2 → 0.3.3
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 +212 -60
- package/branching/index.js +266 -110
- package/caches/index.js +257 -77
- package/changes/index.js +93 -29
- package/contributions/index.js +249 -101
- package/index.js +2 -2
- package/package.json +1 -1
- package/setup/index.js +190 -55
- package/utils/constants.js +15 -0
- package/utils/getStateByHash/index.js +135 -78
- package/utils/shouldIgnore/index.js +44 -3
- package/workflow/index.js +256 -132
|
@@ -1,9 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Artifact - Modern version control.
|
|
3
|
+
* @author Benny Schmidt (https://github.com/bennyschmidt)
|
|
4
|
+
* @project https://github.com/bennyschmidt/artifact
|
|
5
|
+
* Module: Utility / shouldIgnore (v0.3.3)
|
|
6
|
+
*/
|
|
7
|
+
|
|
1
8
|
const fs = require('fs');
|
|
2
9
|
const path = require('path');
|
|
3
10
|
|
|
4
11
|
let memoizedRules = null;
|
|
5
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Parses .artignore and converts glob-like patterns into executable regex.
|
|
15
|
+
* @returns {RegExp[]} An array of compiled regex rules.
|
|
16
|
+
*/
|
|
17
|
+
|
|
6
18
|
function getRules () {
|
|
19
|
+
/**
|
|
20
|
+
* Return cached rules if they have already been processed.
|
|
21
|
+
*/
|
|
22
|
+
|
|
7
23
|
if (memoizedRules) {
|
|
8
24
|
return memoizedRules;
|
|
9
25
|
}
|
|
@@ -12,6 +28,10 @@ function getRules () {
|
|
|
12
28
|
const ignorePath = path.join(root, '.artignore');
|
|
13
29
|
const rules = [];
|
|
14
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Read the ignore file and transform each line into a path-matching regex.
|
|
33
|
+
*/
|
|
34
|
+
|
|
15
35
|
if (fs.existsSync(ignorePath)) {
|
|
16
36
|
const lines = fs.readFileSync(ignorePath, 'utf8').split(/\r?\n/);
|
|
17
37
|
|
|
@@ -22,6 +42,11 @@ function getRules () {
|
|
|
22
42
|
continue;
|
|
23
43
|
}
|
|
24
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Escape special characters and translate wildcards (* and **)
|
|
47
|
+
* into valid regex syntax.
|
|
48
|
+
*/
|
|
49
|
+
|
|
25
50
|
let regexStr = line
|
|
26
51
|
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
27
52
|
.replace(/\/\*\*\//g, '(/.+/|/)')
|
|
@@ -43,16 +68,32 @@ function getRules () {
|
|
|
43
68
|
return rules;
|
|
44
69
|
}
|
|
45
70
|
|
|
46
|
-
|
|
47
|
-
|
|
71
|
+
/**
|
|
72
|
+
* Determines if a given relative path should be excluded from version control.
|
|
73
|
+
* @param {string} relativePath - The path to check.
|
|
74
|
+
* @returns {boolean} True if the path matches an ignore rule.
|
|
75
|
+
*/
|
|
76
|
+
|
|
77
|
+
function shouldIgnore (relativePath) {
|
|
78
|
+
/**
|
|
79
|
+
* Normalize path separators to forward slashes and protect the internal .art folder.
|
|
80
|
+
*/
|
|
81
|
+
|
|
82
|
+
const normalizedPath = relativePath.split(path.sep).join('/');
|
|
48
83
|
|
|
49
84
|
if (normalizedPath === '.art' || normalizedPath.startsWith('.art/')) {
|
|
50
85
|
return true;
|
|
51
86
|
}
|
|
52
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Test the path against the compiled list of ignore patterns.
|
|
90
|
+
*/
|
|
91
|
+
|
|
53
92
|
const rules = getRules();
|
|
54
93
|
|
|
55
|
-
return rules.some(rule =>
|
|
94
|
+
return rules.some(rule => {
|
|
95
|
+
return rule.test(normalizedPath);
|
|
96
|
+
});
|
|
56
97
|
}
|
|
57
98
|
|
|
58
99
|
module.exports = shouldIgnore;
|
package/workflow/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Artifact - Modern version control.
|
|
3
|
+
* @author Benny Schmidt (https://github.com/bennyschmidt)
|
|
4
|
+
* @project https://github.com/bennyschmidt/artifact
|
|
5
|
+
* Module: Workflow (v0.3.3)
|
|
4
6
|
*/
|
|
5
7
|
|
|
6
8
|
const fs = require('fs');
|
|
@@ -9,29 +11,43 @@ const crypto = require('crypto');
|
|
|
9
11
|
|
|
10
12
|
const getStateByHash = require('../utils/getStateByHash');
|
|
11
13
|
const shouldIgnore = require('../utils/shouldIgnore');
|
|
12
|
-
|
|
13
|
-
const MAX_PART_SIZE = 32000000;
|
|
14
|
+
const { MAX_PART_SIZE } = require('../utils/constants');
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Helper to load all changes from a paginated stage directory.
|
|
18
|
+
* @param {string} artifactPath - Path to the .art directory.
|
|
19
|
+
* @returns {Object} All staged changes merged into one object.
|
|
17
20
|
*/
|
|
18
21
|
|
|
19
|
-
function getStagedChanges(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
function getStagedChanges (artifactPath) {
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the stage directory and locate the manifest for assembly.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const stageDirectory = path.join(artifactPath, 'stage');
|
|
28
|
+
const manifestPath = path.join(stageDirectory, 'manifest.json');
|
|
22
29
|
|
|
23
|
-
if (!fs.existsSync(
|
|
30
|
+
if (!fs.existsSync(stageDirectory) || !fs.existsSync(manifestPath)) {
|
|
24
31
|
return {};
|
|
25
32
|
}
|
|
26
33
|
|
|
27
|
-
const manifest = JSON.parse(
|
|
34
|
+
const manifest = JSON.parse(
|
|
35
|
+
fs.readFileSync(manifestPath, 'utf8')
|
|
36
|
+
);
|
|
37
|
+
|
|
28
38
|
let allChanges = {};
|
|
29
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Aggregate changes from all registered manifest parts.
|
|
42
|
+
*/
|
|
43
|
+
|
|
30
44
|
for (const partName of manifest.parts) {
|
|
31
|
-
const partPath = path.join(
|
|
45
|
+
const partPath = path.join(stageDirectory, partName);
|
|
32
46
|
|
|
33
47
|
if (fs.existsSync(partPath)) {
|
|
34
|
-
const partData = JSON.parse(
|
|
48
|
+
const partData = JSON.parse(
|
|
49
|
+
fs.readFileSync(partPath, 'utf8')
|
|
50
|
+
);
|
|
35
51
|
|
|
36
52
|
Object.assign(allChanges, partData.changes);
|
|
37
53
|
}
|
|
@@ -42,35 +58,51 @@ function getStagedChanges(artPath) {
|
|
|
42
58
|
|
|
43
59
|
/**
|
|
44
60
|
* Compares the working directory against the last commit and pending stage.
|
|
61
|
+
* @returns {Object} Status report containing staged, modified, and untracked files.
|
|
45
62
|
*/
|
|
46
63
|
|
|
47
64
|
function status () {
|
|
65
|
+
/**
|
|
66
|
+
* Initialize workspace paths and retrieve active state and staging info.
|
|
67
|
+
*/
|
|
68
|
+
|
|
48
69
|
const root = process.cwd();
|
|
49
|
-
const
|
|
50
|
-
const
|
|
70
|
+
const artifactPath = path.join(root, '.art');
|
|
71
|
+
const artifactJsonPath = path.join(artifactPath, 'art.json');
|
|
51
72
|
|
|
52
|
-
if (!fs.existsSync(
|
|
73
|
+
if (!fs.existsSync(artifactJsonPath)) {
|
|
53
74
|
throw new Error('No art repository found.');
|
|
54
75
|
}
|
|
55
76
|
|
|
56
|
-
const
|
|
57
|
-
|
|
77
|
+
const artifactJson = JSON.parse(
|
|
78
|
+
fs.readFileSync(artifactJsonPath, 'utf8')
|
|
79
|
+
);
|
|
80
|
+
const activeBranch = artifactJson.active.branch;
|
|
58
81
|
|
|
59
|
-
const stagedFiles = getStagedChanges(
|
|
60
|
-
const activeState = getStateByHash(activeBranch,
|
|
82
|
+
const stagedFiles = getStagedChanges(artifactPath);
|
|
83
|
+
const activeState = getStateByHash(activeBranch, artifactJson.active.parent) || {};
|
|
61
84
|
|
|
62
85
|
const allFiles = fs.readdirSync(root, { recursive: true })
|
|
63
|
-
.filter(
|
|
86
|
+
.filter(file => {
|
|
87
|
+
return !fs.statSync(path.join(root, file)).isDirectory();
|
|
88
|
+
});
|
|
64
89
|
|
|
65
90
|
const untracked = [];
|
|
66
91
|
const modified = [];
|
|
67
92
|
const ignored = [];
|
|
68
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Categorize each file by comparing its working directory state
|
|
96
|
+
* to history and staging.
|
|
97
|
+
*/
|
|
98
|
+
|
|
69
99
|
for (const file of allFiles) {
|
|
70
100
|
const isStaged = !!stagedFiles[file];
|
|
71
101
|
const isActive = !!activeState[file];
|
|
72
102
|
|
|
73
|
-
if (file === '.art' || file.startsWith(`.art${path.sep}`))
|
|
103
|
+
if (file === '.art' || file.startsWith(`.art${path.sep}`)) {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
74
106
|
|
|
75
107
|
const isIgnored = shouldIgnore(file);
|
|
76
108
|
|
|
@@ -83,7 +115,10 @@ function status () {
|
|
|
83
115
|
if (!isStaged && !isActive) {
|
|
84
116
|
untracked.push(file);
|
|
85
117
|
} else if (!isStaged && isActive) {
|
|
86
|
-
const currentContent = fs.readFileSync(
|
|
118
|
+
const currentContent = fs.readFileSync(
|
|
119
|
+
path.join(root, file),
|
|
120
|
+
'utf8'
|
|
121
|
+
);
|
|
87
122
|
|
|
88
123
|
if (currentContent !== activeState[file]) {
|
|
89
124
|
modified.push(file);
|
|
@@ -93,7 +128,7 @@ function status () {
|
|
|
93
128
|
|
|
94
129
|
return {
|
|
95
130
|
activeBranch,
|
|
96
|
-
lastCommit:
|
|
131
|
+
lastCommit: artifactJson.active.parent,
|
|
97
132
|
staged: Object.keys(stagedFiles),
|
|
98
133
|
modified,
|
|
99
134
|
untracked,
|
|
@@ -103,144 +138,202 @@ function status () {
|
|
|
103
138
|
|
|
104
139
|
/**
|
|
105
140
|
* Updates or creates a JSON diff in the stage directory.
|
|
141
|
+
* @param {string} targetPath - Path to the file or directory to stage.
|
|
142
|
+
* @returns {string} Success message.
|
|
106
143
|
*/
|
|
107
144
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const artJsonPath = path.join(artPath, 'art.json');
|
|
113
|
-
const fullPath = path.resolve(root, targetPath);
|
|
145
|
+
function add (targetPath) {
|
|
146
|
+
/**
|
|
147
|
+
* Validate target and load current repository configuration.
|
|
148
|
+
*/
|
|
114
149
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
150
|
+
const root = process.cwd();
|
|
151
|
+
const artifactPath = path.join(root, '.art');
|
|
152
|
+
const stageDirectory = path.join(artifactPath, 'stage');
|
|
153
|
+
const artifactJsonPath = path.join(artifactPath, 'art.json');
|
|
154
|
+
const fullPath = path.resolve(root, targetPath);
|
|
118
155
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
156
|
+
if (!fs.existsSync(fullPath)) {
|
|
157
|
+
throw new Error(`Path does not exist: ${targetPath}`);
|
|
158
|
+
}
|
|
122
159
|
|
|
123
|
-
|
|
124
|
-
|
|
160
|
+
const artifactJson = JSON.parse(
|
|
161
|
+
fs.readFileSync(artifactJsonPath, 'utf8')
|
|
162
|
+
);
|
|
163
|
+
const activeState = getStateByHash(artifactJson.active.branch, artifactJson.active.parent) || {};
|
|
164
|
+
const currentStaged = getStagedChanges(artifactPath);
|
|
125
165
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
166
|
+
const stats = fs.statSync(fullPath);
|
|
167
|
+
const relativeTarget = path.relative(root, fullPath);
|
|
129
168
|
|
|
130
|
-
|
|
169
|
+
if (!stats.isDirectory() && shouldIgnore(relativeTarget) && !activeState[relativeTarget]) {
|
|
170
|
+
return `${relativeTarget} is being ignored.`;
|
|
171
|
+
}
|
|
131
172
|
|
|
132
|
-
|
|
133
|
-
filesToProcess = fs.readdirSync(fullPath, { recursive: true })
|
|
134
|
-
.filter(f => {
|
|
135
|
-
const absF = path.join(fullPath, f);
|
|
136
|
-
const relF = path.relative(root, absF);
|
|
173
|
+
let filesToProcess = [];
|
|
137
174
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
} else {
|
|
142
|
-
filesToProcess = [relativeTarget];
|
|
143
|
-
}
|
|
175
|
+
/**
|
|
176
|
+
* Identify all files to be added, respecting ignore rules unless already tracked.
|
|
177
|
+
*/
|
|
144
178
|
|
|
145
|
-
|
|
179
|
+
if (stats.isDirectory()) {
|
|
180
|
+
const rawFiles = fs.readdirSync(fullPath, { recursive: true });
|
|
146
181
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
182
|
+
for (const entry of rawFiles) {
|
|
183
|
+
const absoluteEntry = path.join(fullPath, entry);
|
|
184
|
+
const relativeEntry = path.relative(root, absoluteEntry);
|
|
150
185
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
186
|
+
if (!fs.statSync(absoluteEntry).isDirectory() && !relativeEntry.startsWith('.art')) {
|
|
187
|
+
if (!shouldIgnore(relativeEntry) || !!activeState[relativeEntry]) {
|
|
188
|
+
filesToProcess.push(relativeEntry);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
} else {
|
|
193
|
+
filesToProcess = [relativeTarget];
|
|
194
|
+
}
|
|
155
195
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
196
|
+
if (filesToProcess.length === 0) {
|
|
197
|
+
return 'No changes to add.';
|
|
198
|
+
}
|
|
159
199
|
|
|
160
|
-
|
|
161
|
-
|
|
200
|
+
/**
|
|
201
|
+
* Calculate deltas for each file and update the staging map.
|
|
202
|
+
*/
|
|
203
|
+
|
|
204
|
+
for (const relativePath of filesToProcess) {
|
|
205
|
+
const currentContent = fs.readFileSync(
|
|
206
|
+
path.join(root, relativePath),
|
|
207
|
+
'utf8'
|
|
208
|
+
);
|
|
209
|
+
const previousContent = activeState[relativePath];
|
|
210
|
+
|
|
211
|
+
if (previousContent === undefined) {
|
|
212
|
+
currentStaged[relativePath] = {
|
|
213
|
+
type: 'createFile',
|
|
214
|
+
content: currentContent
|
|
215
|
+
};
|
|
216
|
+
} else if (currentContent !== previousContent) {
|
|
217
|
+
let start = 0;
|
|
218
|
+
|
|
219
|
+
while (start < previousContent.length && start < currentContent.length && previousContent[start] === currentContent[start]) {
|
|
220
|
+
start++;
|
|
221
|
+
}
|
|
162
222
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
223
|
+
let oldEnd = previousContent.length - 1;
|
|
224
|
+
let newEnd = currentContent.length - 1;
|
|
166
225
|
|
|
167
|
-
|
|
168
|
-
|
|
226
|
+
while (oldEnd >= start && newEnd >= start && previousContent[oldEnd] === currentContent[newEnd]) {
|
|
227
|
+
oldEnd--;
|
|
228
|
+
newEnd--;
|
|
229
|
+
}
|
|
169
230
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
type: 'delete',
|
|
173
|
-
position: start,
|
|
174
|
-
length: deletionLength,
|
|
175
|
-
content: previousContent.slice(start, oldEnd + 1)
|
|
176
|
-
});
|
|
177
|
-
}
|
|
231
|
+
const operations = [];
|
|
232
|
+
const deletionLength = oldEnd - start + 1;
|
|
178
233
|
|
|
179
|
-
|
|
234
|
+
if (deletionLength > 0) {
|
|
235
|
+
operations.push({
|
|
236
|
+
type: 'delete',
|
|
237
|
+
position: start,
|
|
238
|
+
length: deletionLength,
|
|
239
|
+
content: previousContent.slice(start, oldEnd + 1)
|
|
240
|
+
});
|
|
241
|
+
}
|
|
180
242
|
|
|
181
|
-
|
|
182
|
-
operations.push({ type: 'insert', position: start, content: insertionContent });
|
|
183
|
-
}
|
|
243
|
+
const insertionContent = currentContent.slice(start, newEnd + 1);
|
|
184
244
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
245
|
+
if (insertionContent.length > 0) {
|
|
246
|
+
operations.push({
|
|
247
|
+
type: 'insert',
|
|
248
|
+
position: start,
|
|
249
|
+
content: insertionContent
|
|
250
|
+
});
|
|
251
|
+
}
|
|
190
252
|
|
|
191
|
-
|
|
253
|
+
if (operations.length > 0) {
|
|
254
|
+
currentStaged[relativePath] = operations;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
192
258
|
|
|
193
|
-
|
|
259
|
+
/**
|
|
260
|
+
* Serialize the staging map into paginated JSON files for efficient storage.
|
|
261
|
+
*/
|
|
194
262
|
|
|
195
|
-
|
|
263
|
+
if (fs.existsSync(stageDirectory)) {
|
|
264
|
+
fs.rmSync(stageDirectory, { recursive: true, force: true });
|
|
265
|
+
}
|
|
196
266
|
|
|
197
|
-
|
|
198
|
-
let currentSize = 0;
|
|
267
|
+
fs.mkdirSync(stageDirectory, { recursive: true });
|
|
199
268
|
|
|
200
|
-
|
|
201
|
-
const partName = `part.${stageParts.length}.json`;
|
|
269
|
+
const stageParts = [];
|
|
202
270
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
271
|
+
let currentPartChanges = {};
|
|
272
|
+
let currentSize = 0;
|
|
273
|
+
|
|
274
|
+
const savePart = () => {
|
|
275
|
+
const partName = `part.${stageParts.length}.json`;
|
|
208
276
|
|
|
209
|
-
|
|
210
|
-
|
|
277
|
+
fs.writeFileSync(
|
|
278
|
+
path.join(stageDirectory, partName),
|
|
279
|
+
JSON.stringify({ changes: currentPartChanges }, null, 2)
|
|
280
|
+
);
|
|
211
281
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
282
|
+
stageParts.push(partName);
|
|
283
|
+
currentPartChanges = {};
|
|
284
|
+
currentSize = 0;
|
|
285
|
+
};
|
|
215
286
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
}
|
|
287
|
+
for (const [file, changes] of Object.entries(currentStaged)) {
|
|
288
|
+
const size = JSON.stringify(changes).length;
|
|
219
289
|
|
|
220
|
-
|
|
290
|
+
if (currentSize + size > MAX_PART_SIZE && Object.keys(currentPartChanges).length > 0) {
|
|
291
|
+
savePart();
|
|
292
|
+
}
|
|
221
293
|
|
|
222
|
-
|
|
294
|
+
currentPartChanges[file] = changes;
|
|
295
|
+
currentSize += size;
|
|
296
|
+
}
|
|
223
297
|
|
|
224
|
-
|
|
225
|
-
|
|
298
|
+
savePart();
|
|
299
|
+
|
|
300
|
+
fs.writeFileSync(
|
|
301
|
+
path.join(stageDirectory, 'manifest.json'),
|
|
302
|
+
JSON.stringify({ parts: stageParts }, null, 2)
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
return `Added ${filesToProcess.length} file(s) to stage.`;
|
|
306
|
+
}
|
|
226
307
|
|
|
227
308
|
/**
|
|
228
309
|
* Finalizes the paginated stage into a paginated commit structure.
|
|
310
|
+
* @param {string} message - The commit message.
|
|
311
|
+
* @returns {string} Success summary.
|
|
229
312
|
*/
|
|
230
313
|
|
|
231
314
|
function commit (message) {
|
|
232
|
-
|
|
315
|
+
/**
|
|
316
|
+
* Validate prerequisites for a commit and calculate the unique commit hash.
|
|
317
|
+
*/
|
|
318
|
+
|
|
319
|
+
if (!message) {
|
|
320
|
+
throw new Error('A commit message is required.');
|
|
321
|
+
}
|
|
233
322
|
|
|
234
|
-
const
|
|
235
|
-
const
|
|
236
|
-
const
|
|
237
|
-
const
|
|
323
|
+
const root = process.cwd();
|
|
324
|
+
const artifactPath = path.join(root, '.art');
|
|
325
|
+
const stageDirectory = path.join(artifactPath, 'stage');
|
|
326
|
+
const artifactJsonPath = path.join(artifactPath, 'art.json');
|
|
238
327
|
|
|
239
|
-
if (!fs.existsSync(
|
|
328
|
+
if (!fs.existsSync(stageDirectory)) {
|
|
329
|
+
throw new Error('Nothing to commit (stage is empty).');
|
|
330
|
+
}
|
|
240
331
|
|
|
241
|
-
const stagedChanges = getStagedChanges(
|
|
242
|
-
const
|
|
243
|
-
|
|
332
|
+
const stagedChanges = getStagedChanges(artifactPath);
|
|
333
|
+
const artifactJson = JSON.parse(
|
|
334
|
+
fs.readFileSync(artifactJsonPath, 'utf8')
|
|
335
|
+
);
|
|
336
|
+
const branch = artifactJson.active.branch;
|
|
244
337
|
const timestamp = Date.now();
|
|
245
338
|
|
|
246
339
|
const commitHash = crypto
|
|
@@ -248,20 +341,29 @@ function commit (message) {
|
|
|
248
341
|
.update(JSON.stringify(stagedChanges) + timestamp + message)
|
|
249
342
|
.digest('hex');
|
|
250
343
|
|
|
251
|
-
const
|
|
344
|
+
const branchHistoryDirectory = path.join(artifactPath, 'history', 'local', branch);
|
|
252
345
|
const commitParts = [];
|
|
253
346
|
|
|
254
347
|
let currentPartChanges = {};
|
|
255
348
|
let currentPartSize = 0;
|
|
256
349
|
|
|
350
|
+
/**
|
|
351
|
+
* Break the commit into paginated parts to handle large datasets.
|
|
352
|
+
*/
|
|
353
|
+
|
|
257
354
|
const saveCommitPart = () => {
|
|
258
|
-
if (Object.keys(currentPartChanges).length === 0)
|
|
355
|
+
if (Object.keys(currentPartChanges).length === 0) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
259
358
|
|
|
260
359
|
const partName = `${commitHash}.part.${commitParts.length}.json`;
|
|
261
360
|
|
|
262
|
-
fs.writeFileSync(
|
|
263
|
-
|
|
361
|
+
fs.writeFileSync(
|
|
362
|
+
path.join(branchHistoryDirectory, partName),
|
|
363
|
+
JSON.stringify({ changes: currentPartChanges }, null, 2)
|
|
364
|
+
);
|
|
264
365
|
|
|
366
|
+
commitParts.push(partName);
|
|
265
367
|
currentPartChanges = {};
|
|
266
368
|
currentPartSize = 0;
|
|
267
369
|
};
|
|
@@ -276,33 +378,55 @@ function commit (message) {
|
|
|
276
378
|
currentPartChanges[filePath] = changeSet;
|
|
277
379
|
currentPartSize += changeSize;
|
|
278
380
|
}
|
|
381
|
+
|
|
279
382
|
saveCommitPart();
|
|
280
383
|
|
|
384
|
+
/**
|
|
385
|
+
* Finalize the commit master file and update the branch manifest.
|
|
386
|
+
*/
|
|
387
|
+
|
|
281
388
|
const commitMaster = {
|
|
282
389
|
hash: commitHash,
|
|
283
390
|
message,
|
|
284
391
|
timestamp,
|
|
285
|
-
parent:
|
|
392
|
+
parent: artifactJson.active.parent,
|
|
286
393
|
parts: commitParts
|
|
287
394
|
};
|
|
288
395
|
|
|
289
|
-
fs.writeFileSync(
|
|
396
|
+
fs.writeFileSync(
|
|
397
|
+
path.join(branchHistoryDirectory, `${commitHash}.json`),
|
|
398
|
+
JSON.stringify(commitMaster, null, 2)
|
|
399
|
+
);
|
|
290
400
|
|
|
291
|
-
const manifest = JSON.parse(
|
|
401
|
+
const manifest = JSON.parse(
|
|
402
|
+
fs.readFileSync(path.join(branchHistoryDirectory, 'manifest.json'), 'utf8')
|
|
403
|
+
);
|
|
292
404
|
|
|
293
405
|
manifest.commits.push(commitHash);
|
|
294
|
-
fs.writeFileSync(path.join(branchHistoryDir, 'manifest.json'), JSON.stringify(manifest, null, 2));
|
|
295
406
|
|
|
296
|
-
|
|
297
|
-
|
|
407
|
+
fs.writeFileSync(
|
|
408
|
+
path.join(branchHistoryDirectory, 'manifest.json'),
|
|
409
|
+
JSON.stringify(manifest, null, 2)
|
|
410
|
+
);
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Update the active parent reference and clear the staging area.
|
|
414
|
+
*/
|
|
415
|
+
|
|
416
|
+
artifactJson.active.parent = commitHash;
|
|
417
|
+
|
|
418
|
+
fs.writeFileSync(
|
|
419
|
+
artifactJsonPath,
|
|
420
|
+
JSON.stringify(artifactJson, null, 2)
|
|
421
|
+
);
|
|
298
422
|
|
|
299
|
-
fs.rmSync(
|
|
423
|
+
fs.rmSync(stageDirectory, { recursive: true, force: true });
|
|
300
424
|
|
|
301
|
-
return `[${branch} ${commitHash.slice(0, 7)}] ${message}
|
|
425
|
+
return `[${branch} ${commitHash.slice(0, 7)}] ${message}`;
|
|
302
426
|
}
|
|
303
427
|
|
|
304
428
|
module.exports = {
|
|
305
|
-
__libraryVersion: '0.3.
|
|
429
|
+
__libraryVersion: '0.3.3',
|
|
306
430
|
__libraryAPIName: 'Workflow',
|
|
307
431
|
status,
|
|
308
432
|
add,
|