pagan-artifact 0.2.9 → 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/bin/art.js +15 -2
- package/branching/index.js +13 -7
- package/caches/index.js +55 -79
- package/changes/index.js +16 -7
- package/contributions/index.js +24 -10
- package/index.js +2 -2
- package/package.json +1 -1
- package/setup/index.js +113 -111
- package/utils/getStateByHash/index.js +1 -1
- package/workflow/index.js +96 -84
package/bin/art.js
CHANGED
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* art - Modern version control.
|
|
5
|
-
* CLI (v0.
|
|
5
|
+
* CLI (v0.3.1)
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
const art = require('../index.js');
|
|
9
|
+
const path = require('path');
|
|
9
10
|
|
|
10
11
|
const [,, command, ...args] = process.argv;
|
|
11
12
|
|
|
@@ -69,7 +70,19 @@ async function run() {
|
|
|
69
70
|
|
|
70
71
|
if (ignored && ignored.length > 0) {
|
|
71
72
|
console.log('\nIgnored files:');
|
|
72
|
-
|
|
73
|
+
|
|
74
|
+
const compressedIgnored = new Set();
|
|
75
|
+
|
|
76
|
+
ignored.forEach(f => {
|
|
77
|
+
const parts = f.split(path.sep);
|
|
78
|
+
if (parts.length > 1) {
|
|
79
|
+
compressedIgnored.add(`${parts[0]}${path.sep}`);
|
|
80
|
+
} else {
|
|
81
|
+
compressedIgnored.add(f);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
compressedIgnored.forEach(f => console.log(`${GRAY}\t${f}${RESET}`));
|
|
73
86
|
}
|
|
74
87
|
|
|
75
88
|
if (untracked.length === 0 && modified.length === 0 && staged.length === 0) {
|
package/branching/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* art - Modern version control.
|
|
3
|
-
* Module: Branching (v0.
|
|
3
|
+
* Module: Branching (v0.3.1)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const fs = require('fs');
|
|
@@ -26,6 +26,7 @@ function branch ({ name, isDelete = false } = {}) {
|
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
const normalizedName = name.toLowerCase();
|
|
29
30
|
const illegalRegExp = /[\/\\]/g;
|
|
30
31
|
const controlRegExp = /[\x00-\x1f\x80-\x9f]/g;
|
|
31
32
|
const reservedRegExp = /^\.+$/;
|
|
@@ -89,20 +90,25 @@ function branch ({ name, isDelete = false } = {}) {
|
|
|
89
90
|
|
|
90
91
|
if (initialCommits.length > 0) {
|
|
91
92
|
const sourceBranchPath = path.join(localHistoryPath, sourceBranchName);
|
|
93
|
+
const sourceRemotePath = path.join(remoteHistoryPath, sourceBranchName);
|
|
92
94
|
|
|
93
95
|
for (const hash of initialCommits) {
|
|
94
|
-
|
|
96
|
+
let masterFile = path.join(sourceBranchPath, `${hash}.json`);
|
|
97
|
+
let currentSrcDir = sourceBranchPath;
|
|
98
|
+
|
|
99
|
+
if (!fs.existsSync(masterFile)) {
|
|
100
|
+
masterFile = path.join(sourceRemotePath, `${hash}.json`);
|
|
101
|
+
currentSrcDir = sourceRemotePath;
|
|
102
|
+
}
|
|
95
103
|
|
|
96
104
|
if (fs.existsSync(masterFile)) {
|
|
97
|
-
// Copy the Master File
|
|
98
105
|
fs.copyFileSync(masterFile, path.join(branchLocalPath, `${hash}.json`));
|
|
99
106
|
|
|
100
|
-
// Detect and copy all Parts
|
|
101
107
|
const commitMaster = JSON.parse(fs.readFileSync(masterFile, 'utf8'));
|
|
102
108
|
|
|
103
109
|
if (commitMaster.parts && Array.isArray(commitMaster.parts)) {
|
|
104
110
|
for (const partName of commitMaster.parts) {
|
|
105
|
-
const srcPart = path.join(
|
|
111
|
+
const srcPart = path.join(currentSrcDir, partName);
|
|
106
112
|
const destPart = path.join(branchLocalPath, partName);
|
|
107
113
|
|
|
108
114
|
if (fs.existsSync(srcPart)) {
|
|
@@ -287,11 +293,11 @@ function checkout (branchName, { force = false } = {}) {
|
|
|
287
293
|
JSON.stringify({ parts: Array.from({ length: partCount }, (_, i) => `part.${i}.json`) }, null, 2)
|
|
288
294
|
);
|
|
289
295
|
|
|
290
|
-
return `Merged ${targetBranch}
|
|
296
|
+
return `Merged ${targetBranch}.`;
|
|
291
297
|
}
|
|
292
298
|
|
|
293
299
|
module.exports = {
|
|
294
|
-
__libraryVersion: '0.
|
|
300
|
+
__libraryVersion: '0.3.1',
|
|
295
301
|
__libraryAPIName: 'Branching',
|
|
296
302
|
branch,
|
|
297
303
|
checkout,
|
package/caches/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* art - Modern version control.
|
|
3
|
-
* Module: Caches (v0.
|
|
3
|
+
* Module: Caches (v0.3.1)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const fs = require('fs');
|
|
@@ -9,15 +9,16 @@ const path = require('path');
|
|
|
9
9
|
const { checkout } = require('../branching/index.js');
|
|
10
10
|
const getStateByHash = require('../utils/getStateByHash');
|
|
11
11
|
|
|
12
|
+
const MAX_PART_SIZE = 32000000;
|
|
13
|
+
|
|
12
14
|
/**
|
|
13
|
-
* Helper to load all changes from a paginated
|
|
15
|
+
* Helper to load all changes from a paginated directory (Stage or Stash).
|
|
14
16
|
*/
|
|
15
17
|
|
|
16
|
-
function
|
|
17
|
-
const
|
|
18
|
-
const manifestPath = path.join(stageDir, 'manifest.json');
|
|
18
|
+
function getPaginatedChanges (dirPath) {
|
|
19
|
+
const manifestPath = path.join(dirPath, 'manifest.json');
|
|
19
20
|
|
|
20
|
-
if (!fs.existsSync(
|
|
21
|
+
if (!fs.existsSync(dirPath) || !fs.existsSync(manifestPath)) {
|
|
21
22
|
return {};
|
|
22
23
|
}
|
|
23
24
|
|
|
@@ -26,7 +27,7 @@ function getStagedChanges(artPath) {
|
|
|
26
27
|
let allChanges = {};
|
|
27
28
|
|
|
28
29
|
for (const partName of manifest.parts) {
|
|
29
|
-
const partPath = path.join(
|
|
30
|
+
const partPath = path.join(dirPath, partName);
|
|
30
31
|
|
|
31
32
|
if (fs.existsSync(partPath)) {
|
|
32
33
|
const partData = JSON.parse(fs.readFileSync(partPath, 'utf8'));
|
|
@@ -39,30 +40,28 @@ function getStagedChanges(artPath) {
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
/**
|
|
42
|
-
* Helper to write changes to a paginated
|
|
43
|
+
* Helper to write changes to a paginated directory.
|
|
43
44
|
*/
|
|
44
45
|
|
|
45
|
-
function
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (fs.existsSync(stageDir)) {
|
|
50
|
-
fs.rmSync(stageDir, { recursive: true, force: true });
|
|
46
|
+
function savePaginatedChanges (dirPath, changes) {
|
|
47
|
+
if (fs.existsSync(dirPath)) {
|
|
48
|
+
fs.rmSync(dirPath, { recursive: true, force: true });
|
|
51
49
|
}
|
|
52
50
|
|
|
53
|
-
fs.mkdirSync(
|
|
51
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
54
52
|
|
|
55
|
-
const
|
|
53
|
+
const parts = [];
|
|
56
54
|
|
|
57
55
|
let currentPartChanges = {};
|
|
58
56
|
let currentSize = 0;
|
|
59
57
|
|
|
60
58
|
const savePart = () => {
|
|
61
|
-
|
|
59
|
+
if (Object.keys(currentPartChanges).length === 0) return;
|
|
62
60
|
|
|
63
|
-
|
|
64
|
-
stageParts.push(partName);
|
|
61
|
+
const partName = `part.${parts.length}.json`;
|
|
65
62
|
|
|
63
|
+
fs.writeFileSync(path.join(dirPath, partName), JSON.stringify({ changes: currentPartChanges }, null, 2));
|
|
64
|
+
parts.push(partName);
|
|
66
65
|
currentPartChanges = {};
|
|
67
66
|
currentSize = 0;
|
|
68
67
|
};
|
|
@@ -79,14 +78,9 @@ function saveStagedChanges(artPath, changes) {
|
|
|
79
78
|
}
|
|
80
79
|
|
|
81
80
|
savePart();
|
|
82
|
-
|
|
83
|
-
fs.writeFileSync(path.join(stageDir, 'manifest.json'), JSON.stringify({ parts: stageParts }, null, 2));
|
|
81
|
+
fs.writeFileSync(path.join(dirPath, 'manifest.json'), JSON.stringify({ parts }, null, 2));
|
|
84
82
|
}
|
|
85
83
|
|
|
86
|
-
/**
|
|
87
|
-
* Moves changes to a cache folder, or restores the most recent stash.
|
|
88
|
-
*/
|
|
89
|
-
|
|
90
84
|
function stash ({ pop = false, list = false } = {}) {
|
|
91
85
|
const root = process.cwd();
|
|
92
86
|
const artPath = path.join(root, '.art');
|
|
@@ -97,14 +91,14 @@ function stash ({ pop = false, list = false } = {}) {
|
|
|
97
91
|
if (list) {
|
|
98
92
|
if (!fs.existsSync(cachePath)) return [];
|
|
99
93
|
|
|
100
|
-
const
|
|
101
|
-
.filter(
|
|
94
|
+
const stashDirs = fs.readdirSync(cachePath)
|
|
95
|
+
.filter(d => d.startsWith('stash_') && fs.statSync(path.join(cachePath, d)).isDirectory())
|
|
102
96
|
.sort();
|
|
103
97
|
|
|
104
|
-
return
|
|
105
|
-
id: `stash@{${
|
|
106
|
-
date: new Date(parseInt(
|
|
107
|
-
|
|
98
|
+
return stashDirs.map((dirName, index) => ({
|
|
99
|
+
id: `stash@{${stashDirs.length - 1 - index}}`,
|
|
100
|
+
date: new Date(parseInt(dirName.replace('stash_', ''))).toLocaleString(),
|
|
101
|
+
dirName
|
|
108
102
|
}));
|
|
109
103
|
}
|
|
110
104
|
|
|
@@ -112,18 +106,16 @@ function stash ({ pop = false, list = false } = {}) {
|
|
|
112
106
|
if (!fs.existsSync(cachePath)) throw new Error('No stashes found.');
|
|
113
107
|
|
|
114
108
|
const stashes = fs.readdirSync(cachePath)
|
|
115
|
-
.filter(
|
|
109
|
+
.filter(d => d.startsWith('stash_') && fs.statSync(path.join(cachePath, d)).isDirectory())
|
|
116
110
|
.sort();
|
|
117
111
|
|
|
118
|
-
if (stashes.length === 0)
|
|
119
|
-
throw new Error('No stashes found.');
|
|
120
|
-
}
|
|
112
|
+
if (stashes.length === 0) throw new Error('No stashes found.');
|
|
121
113
|
|
|
122
|
-
const
|
|
123
|
-
const latestStashPath = path.join(cachePath,
|
|
124
|
-
const
|
|
114
|
+
const latestStashDirName = stashes[stashes.length - 1];
|
|
115
|
+
const latestStashPath = path.join(cachePath, latestStashDirName);
|
|
116
|
+
const stashChanges = getPaginatedChanges(latestStashPath);
|
|
125
117
|
|
|
126
|
-
for (const [filePath, changeSet] of Object.entries(
|
|
118
|
+
for (const [filePath, changeSet] of Object.entries(stashChanges)) {
|
|
127
119
|
const fullPath = path.join(root, filePath);
|
|
128
120
|
|
|
129
121
|
if (Array.isArray(changeSet)) {
|
|
@@ -146,9 +138,8 @@ function stash ({ pop = false, list = false } = {}) {
|
|
|
146
138
|
}
|
|
147
139
|
}
|
|
148
140
|
|
|
149
|
-
fs.
|
|
150
|
-
|
|
151
|
-
return `Restored changes from ${latestStashName}.`;
|
|
141
|
+
fs.rmSync(latestStashPath, { recursive: true, force: true });
|
|
142
|
+
return `Restored changes from ${latestStashDirName}.`;
|
|
152
143
|
}
|
|
153
144
|
|
|
154
145
|
const artJson = JSON.parse(fs.readFileSync(artJsonPath, 'utf8'));
|
|
@@ -160,12 +151,16 @@ function stash ({ pop = false, list = false } = {}) {
|
|
|
160
151
|
const stashChanges = {};
|
|
161
152
|
|
|
162
153
|
for (const file of allWorkDirFiles) {
|
|
163
|
-
const
|
|
154
|
+
const fullPath = path.join(root, file);
|
|
155
|
+
const currentBuffer = fs.readFileSync(fullPath);
|
|
156
|
+
const isBinary = currentBuffer.includes(0);
|
|
157
|
+
|
|
158
|
+
const currentContent = isBinary ? null : currentBuffer.toString('utf8');
|
|
164
159
|
const previousContent = activeState[file];
|
|
165
160
|
|
|
166
161
|
if (previousContent === undefined) {
|
|
167
|
-
stashChanges[file] = { type: 'createFile', content: currentContent };
|
|
168
|
-
} else if (currentContent !== previousContent) {
|
|
162
|
+
stashChanges[file] = { type: 'createFile', content: isBinary ? currentBuffer.toString('base64') : currentContent };
|
|
163
|
+
} else if (currentContent !== previousContent && !isBinary) {
|
|
169
164
|
let start = 0;
|
|
170
165
|
|
|
171
166
|
while (start < previousContent.length && start < currentContent.length && previousContent[start] === currentContent[start]) {
|
|
@@ -189,7 +184,9 @@ function stash ({ pop = false, list = false } = {}) {
|
|
|
189
184
|
|
|
190
185
|
if (insCont.length > 0) ops.push({ type: 'insert', position: start, content: insCont });
|
|
191
186
|
|
|
192
|
-
if (ops.length > 0)
|
|
187
|
+
if (ops.length > 0) {
|
|
188
|
+
stashChanges[file] = ops;
|
|
189
|
+
}
|
|
193
190
|
}
|
|
194
191
|
}
|
|
195
192
|
|
|
@@ -199,15 +196,12 @@ function stash ({ pop = false, list = false } = {}) {
|
|
|
199
196
|
}
|
|
200
197
|
}
|
|
201
198
|
|
|
202
|
-
if (Object.keys(stashChanges).length === 0)
|
|
203
|
-
return 'No local changes to stash.';
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if (!fs.existsSync(cachePath)) fs.mkdirSync(cachePath, { recursive: true });
|
|
199
|
+
if (Object.keys(stashChanges).length === 0) return 'No local changes to stash.';
|
|
207
200
|
|
|
208
201
|
const timestamp = Date.now();
|
|
202
|
+
const newStashPath = path.join(cachePath, `stash_${timestamp}`);
|
|
209
203
|
|
|
210
|
-
|
|
204
|
+
savePaginatedChanges(newStashPath, stashChanges);
|
|
211
205
|
|
|
212
206
|
if (fs.existsSync(stageDir)) {
|
|
213
207
|
fs.rmSync(stageDir, { recursive: true, force: true });
|
|
@@ -215,13 +209,9 @@ function stash ({ pop = false, list = false } = {}) {
|
|
|
215
209
|
|
|
216
210
|
checkout(artJson.active.branch, { force: true });
|
|
217
211
|
|
|
218
|
-
return `Saved working directory changes to stash_${timestamp}
|
|
212
|
+
return `Saved working directory changes to stash_${timestamp} and reverted to clean state.`;
|
|
219
213
|
}
|
|
220
214
|
|
|
221
|
-
/**
|
|
222
|
-
* Wipes the stage and moves the active parent pointer if a hash is provided.
|
|
223
|
-
*/
|
|
224
|
-
|
|
225
215
|
function reset (hash) {
|
|
226
216
|
const artPath = path.join(process.cwd(), '.art');
|
|
227
217
|
const stageDir = path.join(artPath, 'stage');
|
|
@@ -231,18 +221,14 @@ function reset (hash) {
|
|
|
231
221
|
fs.rmSync(stageDir, { recursive: true, force: true });
|
|
232
222
|
}
|
|
233
223
|
|
|
234
|
-
if (!hash)
|
|
235
|
-
return 'Staging area cleared.';
|
|
236
|
-
}
|
|
224
|
+
if (!hash) return 'Staging area cleared.';
|
|
237
225
|
|
|
238
226
|
const artJson = JSON.parse(fs.readFileSync(artJsonPath, 'utf8'));
|
|
239
227
|
const branch = artJson.active.branch;
|
|
240
228
|
const branchPath = path.join(artPath, 'history/local', branch);
|
|
241
229
|
const commitPath = path.join(branchPath, `${hash}.json`);
|
|
242
230
|
|
|
243
|
-
if (!fs.existsSync(commitPath)) {
|
|
244
|
-
throw new Error(`Commit ${hash} not found in branch ${branch}.`);
|
|
245
|
-
}
|
|
231
|
+
if (!fs.existsSync(commitPath)) throw new Error(`Commit ${hash} not found in branch ${branch}.`);
|
|
246
232
|
|
|
247
233
|
artJson.active.parent = hash;
|
|
248
234
|
fs.writeFileSync(artJsonPath, JSON.stringify(artJson, null, 2));
|
|
@@ -258,34 +244,24 @@ function reset (hash) {
|
|
|
258
244
|
|
|
259
245
|
checkout(branch);
|
|
260
246
|
|
|
261
|
-
return `
|
|
247
|
+
return `Branch is now at ${hash.slice(0, 7)}. Working directory updated.`;
|
|
262
248
|
}
|
|
263
249
|
|
|
264
|
-
/**
|
|
265
|
-
* Marks a file for deletion by adding a "deleteFile" entry to the stage.
|
|
266
|
-
*/
|
|
267
|
-
|
|
268
250
|
function rm (filePath) {
|
|
269
251
|
const artPath = path.join(process.cwd(), '.art');
|
|
270
252
|
const fullPath = path.join(process.cwd(), filePath);
|
|
253
|
+
const stage = getPaginatedChanges(path.join(artPath, 'stage'));
|
|
271
254
|
|
|
272
|
-
|
|
255
|
+
stage[filePath] = { type: 'deleteFile' };
|
|
256
|
+
savePaginatedChanges(path.join(artPath, 'stage'), stage);
|
|
273
257
|
|
|
274
|
-
|
|
275
|
-
type: 'deleteFile'
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
saveStagedChanges(artPath, stage);
|
|
279
|
-
|
|
280
|
-
if (fs.existsSync(fullPath)) {
|
|
281
|
-
fs.unlinkSync(fullPath);
|
|
282
|
-
}
|
|
258
|
+
if (fs.existsSync(fullPath)) fs.unlinkSync(fullPath);
|
|
283
259
|
|
|
284
260
|
return `File ${filePath} marked for removal.`;
|
|
285
261
|
}
|
|
286
262
|
|
|
287
263
|
module.exports = {
|
|
288
|
-
__libraryVersion: '0.
|
|
264
|
+
__libraryVersion: '0.3.1',
|
|
289
265
|
__libraryAPIName: 'Caches',
|
|
290
266
|
stash,
|
|
291
267
|
reset,
|
package/changes/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* art - Modern version control.
|
|
3
|
-
* Module: Changes (v0.
|
|
3
|
+
* Module: Changes (v0.3.1)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const fs = require('fs');
|
|
@@ -34,8 +34,6 @@ function log () {
|
|
|
34
34
|
|
|
35
35
|
let output = `Branch: ${branch}\n\n`;
|
|
36
36
|
|
|
37
|
-
// Display commits in reverse chronological order
|
|
38
|
-
|
|
39
37
|
for (let i = manifest.commits.length - 1; i >= 0; i--) {
|
|
40
38
|
const hash = manifest.commits[i];
|
|
41
39
|
const commitMasterPath = path.join(branchPath, `${hash}.json`);
|
|
@@ -57,7 +55,7 @@ function log () {
|
|
|
57
55
|
* @returns {object} Formatted diff output and staged file list.
|
|
58
56
|
*/
|
|
59
57
|
|
|
60
|
-
|
|
58
|
+
function diff () {
|
|
61
59
|
const root = process.cwd();
|
|
62
60
|
const artPath = path.join(root, '.art');
|
|
63
61
|
const artJsonPath = path.join(artPath, 'art.json');
|
|
@@ -78,10 +76,13 @@ function log () {
|
|
|
78
76
|
|
|
79
77
|
for (const filePath of currentFiles) {
|
|
80
78
|
const fullPath = path.join(root, filePath);
|
|
81
|
-
const
|
|
79
|
+
const currentBuffer = fs.readFileSync(fullPath);
|
|
80
|
+
const isBinary = currentBuffer.includes(0);
|
|
81
|
+
|
|
82
|
+
const currentContent = isBinary ? null : currentBuffer.toString('utf8');
|
|
82
83
|
const previousContent = lastCommitState[filePath] || '';
|
|
83
84
|
|
|
84
|
-
if (currentContent !== previousContent) {
|
|
85
|
+
if (!isBinary && currentContent !== previousContent) {
|
|
85
86
|
let start = 0;
|
|
86
87
|
|
|
87
88
|
while (start < previousContent.length && start < currentContent.length && previousContent[start] === currentContent[start]) {
|
|
@@ -101,6 +102,12 @@ function log () {
|
|
|
101
102
|
deleted: previousContent.slice(start, oldEnd + 1),
|
|
102
103
|
added: currentContent.slice(start, newEnd + 1)
|
|
103
104
|
});
|
|
105
|
+
} else if (isBinary) {
|
|
106
|
+
const prevHash = lastCommitState[filePath] ? 'exists' : 'null';
|
|
107
|
+
|
|
108
|
+
if (prevHash === 'null') {
|
|
109
|
+
fileDiffs.push({ file: filePath, added: '<Binary Data>', deleted: '' });
|
|
110
|
+
}
|
|
104
111
|
}
|
|
105
112
|
}
|
|
106
113
|
|
|
@@ -115,8 +122,10 @@ function log () {
|
|
|
115
122
|
|
|
116
123
|
for (const partName of manifest.parts) {
|
|
117
124
|
const partPath = path.join(stageDir, partName);
|
|
125
|
+
|
|
118
126
|
if (fs.existsSync(partPath)) {
|
|
119
127
|
const partData = JSON.parse(fs.readFileSync(partPath, 'utf8'));
|
|
128
|
+
|
|
120
129
|
Object.keys(partData.changes).forEach(file => stagedFilesSet.add(file));
|
|
121
130
|
}
|
|
122
131
|
}
|
|
@@ -127,7 +136,7 @@ function log () {
|
|
|
127
136
|
}
|
|
128
137
|
|
|
129
138
|
module.exports = {
|
|
130
|
-
__libraryVersion: '0.
|
|
139
|
+
__libraryVersion: '0.3.1',
|
|
131
140
|
__libraryAPIName: 'Changes',
|
|
132
141
|
log,
|
|
133
142
|
diff
|
package/contributions/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* art - Modern version control.
|
|
3
|
-
* Module: Contributions (v0.
|
|
3
|
+
* Module: Contributions (v0.3.1)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const fs = require('fs');
|
|
@@ -60,7 +60,10 @@ async function fetchRemote () {
|
|
|
60
60
|
method: 'POST',
|
|
61
61
|
headers: { 'Content-Type': 'application/json' },
|
|
62
62
|
body: JSON.stringify({
|
|
63
|
-
type: 'history',
|
|
63
|
+
type: 'history',
|
|
64
|
+
handle,
|
|
65
|
+
repo,
|
|
66
|
+
branch,
|
|
64
67
|
...(token && { personalAccessToken: token })
|
|
65
68
|
})
|
|
66
69
|
});
|
|
@@ -171,20 +174,22 @@ async function push () {
|
|
|
171
174
|
const handle = remoteParts.pop();
|
|
172
175
|
|
|
173
176
|
const localBranchPath = path.join(artPath, 'history/local', branch);
|
|
177
|
+
const remoteBranchPath = path.join(artPath, 'history/remote', branch);
|
|
174
178
|
const localManifest = JSON.parse(fs.readFileSync(path.join(localBranchPath, 'manifest.json'), 'utf8'));
|
|
175
179
|
|
|
176
180
|
const response = await fetch(`${ARTIFACT_HOST}/manifest`, {
|
|
177
181
|
method: 'POST',
|
|
178
182
|
headers: { 'Content-Type': 'application/json' },
|
|
179
183
|
body: JSON.stringify({
|
|
180
|
-
type: 'history',
|
|
181
|
-
|
|
184
|
+
type: 'history',
|
|
185
|
+
handle,
|
|
186
|
+
repo,
|
|
187
|
+
branch,
|
|
182
188
|
...(token && { personalAccessToken: token })
|
|
183
189
|
})
|
|
184
190
|
});
|
|
185
191
|
|
|
186
192
|
const remoteManifest = await response.json();
|
|
187
|
-
|
|
188
193
|
const missingCommits = localManifest.commits.filter(hash => !remoteManifest.commits.includes(hash));
|
|
189
194
|
|
|
190
195
|
if (missingCommits.length === 0) {
|
|
@@ -208,13 +213,17 @@ async function push () {
|
|
|
208
213
|
}
|
|
209
214
|
}
|
|
210
215
|
|
|
216
|
+
const currentRemoteManifestPath = path.join(remoteBranchPath, 'manifest.json');
|
|
217
|
+
const currentRemoteManifest = fs.existsSync(currentRemoteManifestPath)
|
|
218
|
+
? JSON.parse(fs.readFileSync(currentRemoteManifestPath, 'utf8'))
|
|
219
|
+
: { commits: [] };
|
|
220
|
+
|
|
211
221
|
for (const commitHash of missingCommits) {
|
|
212
222
|
const commitMaster = JSON.parse(fs.readFileSync(path.join(localBranchPath, `${commitHash}.json`), 'utf8'));
|
|
213
223
|
|
|
214
|
-
await fetch(`${ARTIFACT_HOST}/push`, {
|
|
224
|
+
const pushRes = await fetch(`${ARTIFACT_HOST}/push`, {
|
|
215
225
|
method: 'POST',
|
|
216
226
|
headers: { 'Content-Type': 'application/json' },
|
|
217
|
-
|
|
218
227
|
body: JSON.stringify({
|
|
219
228
|
handle, repo, branch, commit: commitMaster,
|
|
220
229
|
|
|
@@ -224,12 +233,14 @@ async function push () {
|
|
|
224
233
|
})
|
|
225
234
|
});
|
|
226
235
|
|
|
236
|
+
if (!pushRes.ok) throw new Error(`Server rejected push for commit ${commitHash}`);
|
|
237
|
+
|
|
227
238
|
rootData = null;
|
|
228
239
|
|
|
229
240
|
for (const partName of commitMaster.parts) {
|
|
230
241
|
const partData = JSON.parse(fs.readFileSync(path.join(localBranchPath, partName), 'utf8'));
|
|
231
242
|
|
|
232
|
-
await fetch(`${ARTIFACT_HOST}/commit/part`, {
|
|
243
|
+
const partRes = await fetch(`${ARTIFACT_HOST}/commit/part`, {
|
|
233
244
|
method: 'POST',
|
|
234
245
|
headers: { 'Content-Type': 'application/json' },
|
|
235
246
|
body: JSON.stringify({
|
|
@@ -237,10 +248,13 @@ async function push () {
|
|
|
237
248
|
...(token && { personalAccessToken: token })
|
|
238
249
|
})
|
|
239
250
|
});
|
|
251
|
+
|
|
252
|
+
if (!partRes.ok) throw new Error(`Server failed to receive part ${partName}`);
|
|
240
253
|
}
|
|
241
|
-
}
|
|
242
254
|
|
|
243
|
-
|
|
255
|
+
currentRemoteManifest.commits.push(commitHash);
|
|
256
|
+
fs.writeFileSync(currentRemoteManifestPath, JSON.stringify(currentRemoteManifest, null, 2));
|
|
257
|
+
}
|
|
244
258
|
|
|
245
259
|
return `Pushed ${missingCommits.length} commits to remote.`;
|
|
246
260
|
}
|
package/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* art - Modern version control.
|
|
3
|
-
* Core Library Entry Point (v0.
|
|
3
|
+
* Core Library Entry Point (v0.3.1)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const Setup = require('./setup');
|
|
@@ -50,7 +50,7 @@ const art = {
|
|
|
50
50
|
|
|
51
51
|
// Metadata
|
|
52
52
|
|
|
53
|
-
version: '0.
|
|
53
|
+
version: '0.3.1',
|
|
54
54
|
modules: [
|
|
55
55
|
Setup.__libraryAPIName,
|
|
56
56
|
Workflow.__libraryAPIName,
|
package/package.json
CHANGED
package/setup/index.js
CHANGED
|
@@ -1,137 +1,135 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* art - Modern version control.
|
|
3
|
-
* Module: Setup (v0.
|
|
3
|
+
* Module: Setup (v0.3.1)
|
|
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';
|
|
13
|
+
const MAX_PART_CHARS = 32000000;
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
16
|
+
* Internal helper to create the .art directory tree.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
19
|
+
function ensureDirStructure(artDirectory) {
|
|
20
|
+
const folders = [
|
|
21
|
+
'',
|
|
22
|
+
'root',
|
|
23
|
+
'history',
|
|
24
|
+
'history/local',
|
|
25
|
+
'history/local/main',
|
|
26
|
+
'history/remote',
|
|
27
|
+
'history/remote/main'
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
for (const folder of folders) {
|
|
31
|
+
const fullPath = path.join(artDirectory, folder);
|
|
32
|
+
|
|
33
|
+
if (!fs.existsSync(fullPath)) {
|
|
34
|
+
fs.mkdirSync(fullPath, { recursive: true });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Initializes the local .art directory structure and indexes current files.
|
|
41
|
+
*/
|
|
38
42
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
+
function init (directoryPath = process.cwd()) {
|
|
44
|
+
const artDirectory = path.join(directoryPath, '.art');
|
|
43
45
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
if (fs.existsSync(artDirectory)) {
|
|
47
|
+
return `Reinitialized existing art repository in ${artDirectory}.`;
|
|
48
|
+
}
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
});
|
|
50
|
+
ensureDirStructure(artDirectory);
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
const files = fs.readdirSync(directoryPath, { recursive: true })
|
|
53
|
+
.filter(f => {
|
|
54
|
+
const isInternal = f === '.art' || f.startsWith('.art' + path.sep);
|
|
52
55
|
|
|
53
|
-
|
|
54
|
-
|
|
56
|
+
return !isInternal && !shouldIgnore(f);
|
|
57
|
+
});
|
|
55
58
|
|
|
56
|
-
|
|
59
|
+
const rootMasterManifest = { parts: [] };
|
|
57
60
|
|
|
58
|
-
|
|
59
|
-
|
|
61
|
+
let currentPartFiles = [];
|
|
62
|
+
let currentPartChars = 0;
|
|
60
63
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const partPath = path.join(artDirectory, 'root', partName);
|
|
64
|
+
const saveManifestPart = () => {
|
|
65
|
+
if (currentPartFiles.length === 0) return;
|
|
64
66
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
);
|
|
67
|
+
const partIndex = rootMasterManifest.parts.length;
|
|
68
|
+
const partName = `manifest.part.${partIndex}.json`;
|
|
69
|
+
const partPath = path.join(artDirectory, 'root', partName);
|
|
69
70
|
|
|
70
|
-
|
|
71
|
+
fs.writeFileSync(
|
|
72
|
+
partPath,
|
|
73
|
+
JSON.stringify({ files: currentPartFiles }, null, 2)
|
|
74
|
+
);
|
|
71
75
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
76
|
+
rootMasterManifest.parts.push(partName);
|
|
77
|
+
currentPartFiles = [];
|
|
78
|
+
currentPartChars = 0;
|
|
79
|
+
};
|
|
75
80
|
|
|
76
|
-
|
|
77
|
-
|
|
81
|
+
for (const file of files) {
|
|
82
|
+
const fullPath = path.join(directoryPath, file);
|
|
78
83
|
|
|
79
|
-
|
|
80
|
-
|
|
84
|
+
if (fs.lstatSync(fullPath).isFile()) {
|
|
85
|
+
const content = fs.readFileSync(fullPath, 'utf8');
|
|
81
86
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
87
|
+
if (currentPartChars + content.length > MAX_PART_CHARS && currentPartFiles.length > 0) {
|
|
88
|
+
saveManifestPart();
|
|
89
|
+
}
|
|
85
90
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
91
|
+
currentPartFiles.push({
|
|
92
|
+
path: file,
|
|
93
|
+
content: content
|
|
94
|
+
});
|
|
90
95
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
96
|
+
currentPartChars += content.length;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
94
99
|
|
|
95
|
-
|
|
100
|
+
saveManifestPart();
|
|
96
101
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
102
|
+
fs.writeFileSync(
|
|
103
|
+
path.join(artDirectory, 'root/manifest.json'),
|
|
104
|
+
JSON.stringify(rootMasterManifest, null, 2)
|
|
105
|
+
);
|
|
101
106
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
107
|
+
fs.writeFileSync(
|
|
108
|
+
path.join(artDirectory, 'history/local/main/manifest.json'),
|
|
109
|
+
JSON.stringify({ commits: [] }, null, 2)
|
|
110
|
+
);
|
|
106
111
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
112
|
+
fs.writeFileSync(
|
|
113
|
+
path.join(artDirectory, 'history/remote/main/manifest.json'),
|
|
114
|
+
JSON.stringify({ commits: [] }, null, 2)
|
|
115
|
+
);
|
|
111
116
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
+
const artFile = {
|
|
118
|
+
active: { branch: 'main', parent: null },
|
|
119
|
+
remote: '',
|
|
120
|
+
configuration: { handle: '', personalAccessToken: '' }
|
|
121
|
+
};
|
|
117
122
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
123
|
+
fs.writeFileSync(
|
|
124
|
+
path.join(artDirectory, 'art.json'),
|
|
125
|
+
JSON.stringify(artFile, null, 2)
|
|
126
|
+
);
|
|
122
127
|
|
|
123
|
-
|
|
124
|
-
|
|
128
|
+
return `Initialized empty art repository in ${artDirectory}.`;
|
|
129
|
+
}
|
|
125
130
|
|
|
126
131
|
/**
|
|
127
|
-
* Clone a repository
|
|
128
|
-
* @param {string} repoSlug - The handle/repo identifier.
|
|
129
|
-
* @param {string} providedToken - Optional token for authentication.
|
|
130
|
-
*/
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* art - Modern version control.
|
|
134
|
-
* Module: Setup (v0.2.9) - Clone logic
|
|
132
|
+
* Clone a repository and replay history.
|
|
135
133
|
*/
|
|
136
134
|
|
|
137
135
|
async function clone (repoSlug, providedToken = null) {
|
|
@@ -148,19 +146,20 @@ async function clone (repoSlug, providedToken = null) {
|
|
|
148
146
|
}
|
|
149
147
|
|
|
150
148
|
fs.mkdirSync(targetPath, { recursive: true });
|
|
151
|
-
|
|
149
|
+
|
|
150
|
+
const artPath = path.join(targetPath, '.art');
|
|
151
|
+
|
|
152
|
+
ensureDirStructure(artPath);
|
|
152
153
|
process.chdir(targetPath);
|
|
153
154
|
|
|
154
155
|
try {
|
|
155
|
-
const artPath = path.join(targetPath, '.art');
|
|
156
156
|
const artJsonPath = path.join(artPath, 'art.json');
|
|
157
|
-
const artJson = JSON.parse(fs.readFileSync(artJsonPath, 'utf8'));
|
|
158
157
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
158
|
+
const artJson = {
|
|
159
|
+
active: { branch: 'main', parent: null },
|
|
160
|
+
remote: `${ARTIFACT_HOST}/${handle}/${repo}`,
|
|
161
|
+
configuration: { handle: '', personalAccessToken: providedToken || '' }
|
|
162
|
+
};
|
|
164
163
|
|
|
165
164
|
fs.writeFileSync(artJsonPath, JSON.stringify(artJson, null, 2));
|
|
166
165
|
|
|
@@ -174,7 +173,6 @@ async function clone (repoSlug, providedToken = null) {
|
|
|
174
173
|
handle,
|
|
175
174
|
repo,
|
|
176
175
|
branch: 'main',
|
|
177
|
-
|
|
178
176
|
...(token && { personalAccessToken: token })
|
|
179
177
|
})
|
|
180
178
|
});
|
|
@@ -209,7 +207,6 @@ async function clone (repoSlug, providedToken = null) {
|
|
|
209
207
|
});
|
|
210
208
|
|
|
211
209
|
const historyManifest = await historyRes.json();
|
|
212
|
-
|
|
213
210
|
const localHistoryDir = path.join(artPath, 'history/local/main');
|
|
214
211
|
const remoteHistoryDir = path.join(artPath, 'history/remote/main');
|
|
215
212
|
|
|
@@ -217,10 +214,19 @@ async function clone (repoSlug, providedToken = null) {
|
|
|
217
214
|
const commitRes = await fetch(`${ARTIFACT_HOST}/commit`, {
|
|
218
215
|
method: 'POST',
|
|
219
216
|
headers: { 'Content-Type': 'application/json' },
|
|
220
|
-
body: JSON.stringify({
|
|
217
|
+
body: JSON.stringify({
|
|
218
|
+
handle,
|
|
219
|
+
repo,
|
|
220
|
+
branch: 'main',
|
|
221
|
+
hash: commitHash,
|
|
222
|
+
|
|
223
|
+
...(token && { personalAccessToken: token
|
|
224
|
+
})
|
|
225
|
+
})
|
|
221
226
|
});
|
|
222
227
|
|
|
223
228
|
const commitMaster = await commitRes.json();
|
|
229
|
+
|
|
224
230
|
let fullChanges = {};
|
|
225
231
|
|
|
226
232
|
if (commitMaster.parts && commitMaster.parts.length > 0) {
|
|
@@ -232,7 +238,6 @@ async function clone (repoSlug, providedToken = null) {
|
|
|
232
238
|
});
|
|
233
239
|
|
|
234
240
|
const partData = await partRes.json();
|
|
235
|
-
|
|
236
241
|
fs.writeFileSync(path.join(localHistoryDir, partName), JSON.stringify(partData, null, 2));
|
|
237
242
|
fs.writeFileSync(path.join(remoteHistoryDir, partName), JSON.stringify(partData, null, 2));
|
|
238
243
|
|
|
@@ -252,7 +257,6 @@ async function clone (repoSlug, providedToken = null) {
|
|
|
252
257
|
|
|
253
258
|
if (Array.isArray(changeSet)) {
|
|
254
259
|
let content = fs.existsSync(fullPath) ? fs.readFileSync(fullPath, 'utf8') : '';
|
|
255
|
-
|
|
256
260
|
for (const op of changeSet) {
|
|
257
261
|
if (op.type === 'insert') {
|
|
258
262
|
content = content.slice(0, op.position) + op.content + content.slice(op.position);
|
|
@@ -260,7 +264,6 @@ async function clone (repoSlug, providedToken = null) {
|
|
|
260
264
|
content = content.slice(0, op.position) + content.slice(op.position + op.length);
|
|
261
265
|
}
|
|
262
266
|
}
|
|
263
|
-
|
|
264
267
|
fs.writeFileSync(fullPath, content);
|
|
265
268
|
} else if (changeSet.type === 'createFile') {
|
|
266
269
|
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
@@ -292,8 +295,7 @@ async function clone (repoSlug, providedToken = null) {
|
|
|
292
295
|
/**
|
|
293
296
|
* Updates the configuration in art.json.
|
|
294
297
|
*/
|
|
295
|
-
|
|
296
|
-
function config (key, value) {
|
|
298
|
+
function config(key, value) {
|
|
297
299
|
const manifestPath = path.join(process.cwd(), '.art', 'art.json');
|
|
298
300
|
|
|
299
301
|
if (!fs.existsSync(manifestPath)) {
|
|
@@ -304,7 +306,6 @@ function config (key, value) {
|
|
|
304
306
|
|
|
305
307
|
if (key && value !== undefined) {
|
|
306
308
|
manifest.configuration[key] = value;
|
|
307
|
-
|
|
308
309
|
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
309
310
|
}
|
|
310
311
|
|
|
@@ -316,5 +317,6 @@ module.exports = {
|
|
|
316
317
|
__libraryAPIName: 'Setup',
|
|
317
318
|
init,
|
|
318
319
|
clone,
|
|
319
|
-
config
|
|
320
|
+
config,
|
|
321
|
+
ensureDirStructure
|
|
320
322
|
};
|
package/workflow/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* art - Modern version control.
|
|
3
|
-
* Module: Workflow (v0.
|
|
3
|
+
* Module: Workflow (v0.3.1)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const fs = require('fs');
|
|
@@ -10,6 +10,8 @@ const crypto = require('crypto');
|
|
|
10
10
|
const getStateByHash = require('../utils/getStateByHash');
|
|
11
11
|
const shouldIgnore = require('../utils/shouldIgnore');
|
|
12
12
|
|
|
13
|
+
const MAX_PART_SIZE = 32000000;
|
|
14
|
+
|
|
13
15
|
/**
|
|
14
16
|
* Helper to load all changes from a paginated stage directory.
|
|
15
17
|
*/
|
|
@@ -27,8 +29,10 @@ function getStagedChanges(artPath) {
|
|
|
27
29
|
|
|
28
30
|
for (const partName of manifest.parts) {
|
|
29
31
|
const partPath = path.join(stageDir, partName);
|
|
32
|
+
|
|
30
33
|
if (fs.existsSync(partPath)) {
|
|
31
34
|
const partData = JSON.parse(fs.readFileSync(partPath, 'utf8'));
|
|
35
|
+
|
|
32
36
|
Object.assign(allChanges, partData.changes);
|
|
33
37
|
}
|
|
34
38
|
}
|
|
@@ -72,6 +76,7 @@ function status () {
|
|
|
72
76
|
|
|
73
77
|
if (isIgnored && !isActive && !isStaged) {
|
|
74
78
|
ignored.push(file);
|
|
79
|
+
|
|
75
80
|
continue;
|
|
76
81
|
}
|
|
77
82
|
|
|
@@ -79,6 +84,7 @@ function status () {
|
|
|
79
84
|
untracked.push(file);
|
|
80
85
|
} else if (!isStaged && isActive) {
|
|
81
86
|
const currentContent = fs.readFileSync(path.join(root, file), 'utf8');
|
|
87
|
+
|
|
82
88
|
if (currentContent !== activeState[file]) {
|
|
83
89
|
modified.push(file);
|
|
84
90
|
}
|
|
@@ -99,118 +105,124 @@ function status () {
|
|
|
99
105
|
* Updates or creates a JSON diff in the stage directory.
|
|
100
106
|
*/
|
|
101
107
|
|
|
102
|
-
function add (targetPath) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const fullPath = path.resolve(root, targetPath);
|
|
108
|
+
function add (targetPath) {
|
|
109
|
+
const root = process.cwd();
|
|
110
|
+
const artPath = path.join(root, '.art');
|
|
111
|
+
const stageDir = path.join(artPath, 'stage');
|
|
112
|
+
const artJsonPath = path.join(artPath, 'art.json');
|
|
113
|
+
const fullPath = path.resolve(root, targetPath);
|
|
109
114
|
|
|
110
|
-
|
|
115
|
+
if (!fs.existsSync(fullPath)) {
|
|
116
|
+
throw new Error(`Path does not exist: ${targetPath}`);
|
|
117
|
+
}
|
|
111
118
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
119
|
+
const artJson = JSON.parse(fs.readFileSync(artJsonPath, 'utf8'));
|
|
120
|
+
const activeState = getStateByHash(artJson.active.branch, artJson.active.parent) || {};
|
|
121
|
+
const currentStaged = getStagedChanges(artPath);
|
|
115
122
|
|
|
116
|
-
|
|
117
|
-
|
|
123
|
+
const stats = fs.statSync(fullPath);
|
|
124
|
+
const relativeTarget = path.relative(root, fullPath);
|
|
118
125
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
126
|
+
if (!stats.isDirectory() && shouldIgnore(relativeTarget) && !activeState[relativeTarget]) {
|
|
127
|
+
return `${relativeTarget} is being ignored.`;
|
|
128
|
+
}
|
|
122
129
|
|
|
123
|
-
|
|
130
|
+
let filesToProcess = [];
|
|
124
131
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
132
|
+
if (stats.isDirectory()) {
|
|
133
|
+
filesToProcess = fs.readdirSync(fullPath, { recursive: true })
|
|
134
|
+
.filter(f => {
|
|
135
|
+
const absF = path.join(fullPath, f);
|
|
136
|
+
const relF = path.relative(root, absF);
|
|
130
137
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
138
|
+
return !fs.statSync(absF).isDirectory() && !relF.startsWith('.art') && (!shouldIgnore(relF) || !!activeState[relF]);
|
|
139
|
+
})
|
|
140
|
+
.map(f => path.relative(root, path.join(fullPath, f)));
|
|
141
|
+
} else {
|
|
142
|
+
filesToProcess = [relativeTarget];
|
|
143
|
+
}
|
|
137
144
|
|
|
138
|
-
|
|
145
|
+
if (filesToProcess.length === 0) return "No changes to add.";
|
|
139
146
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
147
|
+
for (const relPath of filesToProcess) {
|
|
148
|
+
const currentContent = fs.readFileSync(path.join(root, relPath), 'utf8');
|
|
149
|
+
const previousContent = activeState[relPath];
|
|
143
150
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
151
|
+
if (previousContent === undefined) {
|
|
152
|
+
currentStaged[relPath] = { type: 'createFile', content: currentContent };
|
|
153
|
+
} else if (currentContent !== previousContent) {
|
|
154
|
+
let start = 0;
|
|
148
155
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
156
|
+
while (start < previousContent.length && start < currentContent.length && previousContent[start] === currentContent[start]) {
|
|
157
|
+
start++;
|
|
158
|
+
}
|
|
152
159
|
|
|
153
|
-
|
|
154
|
-
|
|
160
|
+
let oldEnd = previousContent.length - 1;
|
|
161
|
+
let newEnd = currentContent.length - 1;
|
|
155
162
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
163
|
+
while (oldEnd >= start && newEnd >= start && previousContent[oldEnd] === currentContent[newEnd]) {
|
|
164
|
+
oldEnd--; newEnd--;
|
|
165
|
+
}
|
|
159
166
|
|
|
160
|
-
|
|
161
|
-
|
|
167
|
+
const operations = [];
|
|
168
|
+
const deletionLength = oldEnd - start + 1;
|
|
162
169
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
170
|
+
if (deletionLength > 0) {
|
|
171
|
+
operations.push({
|
|
172
|
+
type: 'delete',
|
|
173
|
+
position: start,
|
|
174
|
+
length: deletionLength,
|
|
175
|
+
content: previousContent.slice(start, oldEnd + 1)
|
|
176
|
+
});
|
|
177
|
+
}
|
|
166
178
|
|
|
167
|
-
|
|
179
|
+
const insertionContent = currentContent.slice(start, newEnd + 1);
|
|
168
180
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
181
|
+
if (insertionContent.length > 0) {
|
|
182
|
+
operations.push({ type: 'insert', position: start, content: insertionContent });
|
|
183
|
+
}
|
|
172
184
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
185
|
+
if (operations.length > 0) {
|
|
186
|
+
currentStaged[relPath] = operations;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
178
190
|
|
|
179
|
-
|
|
191
|
+
if (fs.existsSync(stageDir)) fs.rmSync(stageDir, { recursive: true, force: true });
|
|
180
192
|
|
|
181
|
-
|
|
193
|
+
fs.mkdirSync(stageDir, { recursive: true });
|
|
182
194
|
|
|
183
|
-
|
|
195
|
+
const stageParts = [];
|
|
184
196
|
|
|
185
|
-
|
|
186
|
-
|
|
197
|
+
let currentPartChanges = {};
|
|
198
|
+
let currentSize = 0;
|
|
187
199
|
|
|
188
|
-
|
|
189
|
-
|
|
200
|
+
const savePart = () => {
|
|
201
|
+
const partName = `part.${stageParts.length}.json`;
|
|
190
202
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
203
|
+
fs.writeFileSync(path.join(stageDir, partName), JSON.stringify({ changes: currentPartChanges }, null, 2));
|
|
204
|
+
stageParts.push(partName);
|
|
205
|
+
currentPartChanges = {};
|
|
206
|
+
currentSize = 0;
|
|
207
|
+
};
|
|
196
208
|
|
|
197
|
-
|
|
198
|
-
|
|
209
|
+
for (const [file, changes] of Object.entries(currentStaged)) {
|
|
210
|
+
const size = JSON.stringify(changes).length;
|
|
199
211
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
212
|
+
if (currentSize + size > MAX_PART_SIZE && Object.keys(currentPartChanges).length > 0) {
|
|
213
|
+
savePart();
|
|
214
|
+
}
|
|
203
215
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
216
|
+
currentPartChanges[file] = changes;
|
|
217
|
+
currentSize += size;
|
|
218
|
+
}
|
|
207
219
|
|
|
208
|
-
|
|
220
|
+
savePart();
|
|
209
221
|
|
|
210
|
-
|
|
222
|
+
fs.writeFileSync(path.join(stageDir, 'manifest.json'), JSON.stringify({ parts: stageParts }, null, 2));
|
|
211
223
|
|
|
212
|
-
|
|
213
|
-
}
|
|
224
|
+
return `Added ${filesToProcess.length} file(s) to stage.`;
|
|
225
|
+
}
|
|
214
226
|
|
|
215
227
|
/**
|
|
216
228
|
* Finalizes the paginated stage into a paginated commit structure.
|
|
@@ -290,7 +302,7 @@ function commit (message) {
|
|
|
290
302
|
}
|
|
291
303
|
|
|
292
304
|
module.exports = {
|
|
293
|
-
__libraryVersion: '0.
|
|
305
|
+
__libraryVersion: '0.3.1',
|
|
294
306
|
__libraryAPIName: 'Workflow',
|
|
295
307
|
status,
|
|
296
308
|
add,
|