@dotinc/ogre 0.1.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/.nyc_output/34733e82-2b34-4674-aa72-94cab1c84e85.json +1 -0
- package/.nyc_output/3fb03278-d8e2-4673-abde-3cce50eb4143.json +1 -0
- package/.nyc_output/438de35e-20e6-470d-bfdb-d9b5fbc62f85.json +1 -0
- package/.nyc_output/61ba5903-4249-44c6-afe4-60e536bb69e0.json +1 -0
- package/.nyc_output/67c0b0a0-d3e0-4e7d-85e9-140cf10bf6e9.json +1 -0
- package/.nyc_output/77ec015f-70ab-47be-9757-17b5a39f4100.json +1 -0
- package/.nyc_output/processinfo/34733e82-2b34-4674-aa72-94cab1c84e85.json +1 -0
- package/.nyc_output/processinfo/3fb03278-d8e2-4673-abde-3cce50eb4143.json +1 -0
- package/.nyc_output/processinfo/438de35e-20e6-470d-bfdb-d9b5fbc62f85.json +1 -0
- package/.nyc_output/processinfo/61ba5903-4249-44c6-afe4-60e536bb69e0.json +1 -0
- package/.nyc_output/processinfo/67c0b0a0-d3e0-4e7d-85e9-140cf10bf6e9.json +1 -0
- package/.nyc_output/processinfo/77ec015f-70ab-47be-9757-17b5a39f4100.json +1 -0
- package/.nyc_output/processinfo/index.json +1 -0
- package/.turbo/turbo-build.log +5 -0
- package/CHANGELOG.md +22 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/commit.ts.html +220 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/hash.ts.html +331 -0
- package/coverage/lcov-report/index.html +176 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/ref.ts.html +115 -0
- package/coverage/lcov-report/repository.ts.html +1648 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +196 -0
- package/coverage/lcov-report/test.utils.ts.html +223 -0
- package/coverage/lcov.info +660 -0
- package/lib/commit.d.ts +18 -0
- package/lib/commit.js +8 -0
- package/lib/hash.d.ts +23 -0
- package/lib/hash.js +82 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.js +7 -0
- package/lib/interfaces.d.ts +14 -0
- package/lib/interfaces.js +2 -0
- package/lib/ref.d.ts +2 -0
- package/lib/ref.js +13 -0
- package/lib/repository.d.ts +29 -0
- package/lib/repository.js +455 -0
- package/lib/size.d.ts +4 -0
- package/lib/size.js +31 -0
- package/lib/test.utils.d.ts +17 -0
- package/lib/test.utils.js +41 -0
- package/package.json +49 -0
- package/src/branch.test.ts +58 -0
- package/src/checkout.test.ts +101 -0
- package/src/commit.test.ts +168 -0
- package/src/commit.ts +45 -0
- package/src/hash.ts +82 -0
- package/src/index.ts +5 -0
- package/src/interfaces.ts +19 -0
- package/src/merge.test.ts +38 -0
- package/src/ref.ts +10 -0
- package/src/repository.test.ts +26 -0
- package/src/repository.ts +521 -0
- package/src/size.ts +26 -0
- package/src/test.utils.ts +46 -0
- package/tsconfig.build.json +6 -0
- package/tsconfig.json +16 -0
package/lib/hash.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Credit goes to https://github.com/juanelas/object-sha
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* This module runs perfectly in node.js and browsers
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.digest = void 0;
|
|
12
|
+
/**
|
|
13
|
+
* Returns a string with a hexadecimal representation of the digest of the input object using a given hash algorithm.
|
|
14
|
+
* It first creates an array of the object values ordered by the object keys (using hashable(obj));
|
|
15
|
+
* then, it JSON.stringify-es it; and finally it hashes it.
|
|
16
|
+
*
|
|
17
|
+
* @param obj - An Object
|
|
18
|
+
* @param algorithm - For compatibility with browsers it should be 'SHA-1', 'SHA-256', 'SHA-384' and 'SHA-512'.
|
|
19
|
+
*
|
|
20
|
+
* @param isBrowser
|
|
21
|
+
* @throws {RangeError}
|
|
22
|
+
* Thrown if an invalid hash algorithm is selected.
|
|
23
|
+
*
|
|
24
|
+
* @returns a promise that resolves to a string with hexadecimal content.
|
|
25
|
+
*/
|
|
26
|
+
function digest(obj, algorithm = 'SHA-256', isBrowser = false) {
|
|
27
|
+
const algorithms = ['SHA-1', 'SHA-256', 'SHA-384', 'SHA-512'];
|
|
28
|
+
if (!algorithms.includes(algorithm)) {
|
|
29
|
+
throw RangeError(`Valid hash algorithm values are any of ${JSON.stringify(algorithms)}`);
|
|
30
|
+
}
|
|
31
|
+
return (async function (obj, algorithm) {
|
|
32
|
+
const encoder = new TextEncoder();
|
|
33
|
+
const hashInput = encoder.encode(hashable(obj)).buffer;
|
|
34
|
+
let digest = '';
|
|
35
|
+
if (isBrowser) {
|
|
36
|
+
const buf = await crypto.subtle.digest(algorithm, hashInput);
|
|
37
|
+
const h = '0123456789abcdef';
|
|
38
|
+
(new Uint8Array(buf)).forEach((v) => {
|
|
39
|
+
digest += h[v >> 4] + h[v & 15];
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
const nodeAlg = algorithm.toLowerCase().replace('-', '');
|
|
44
|
+
digest = require('crypto').createHash(nodeAlg).update(Buffer.from(hashInput)).digest('hex'); // eslint-disable-line
|
|
45
|
+
}
|
|
46
|
+
/* eslint-enable no-lone-blocks */
|
|
47
|
+
return digest;
|
|
48
|
+
})(obj, algorithm);
|
|
49
|
+
}
|
|
50
|
+
exports.digest = digest;
|
|
51
|
+
function isObject(val) {
|
|
52
|
+
return (val != null) && (typeof val === 'object') && !(Array.isArray(val));
|
|
53
|
+
}
|
|
54
|
+
function objectToArraySortedByKey(obj) {
|
|
55
|
+
if (!isObject(obj) && !Array.isArray(obj)) {
|
|
56
|
+
return obj;
|
|
57
|
+
}
|
|
58
|
+
if (Array.isArray(obj)) {
|
|
59
|
+
return obj.map((item) => {
|
|
60
|
+
if (Array.isArray(item) || isObject(item)) {
|
|
61
|
+
return objectToArraySortedByKey(item);
|
|
62
|
+
}
|
|
63
|
+
return item;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
// if it is an object convert to array and sort
|
|
67
|
+
return Object.keys(obj) // eslint-disable-line
|
|
68
|
+
.sort()
|
|
69
|
+
.map((key) => {
|
|
70
|
+
return [key, objectToArraySortedByKey(obj[key])];
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* If the input object is not an Array, this function converts the object to an array, all the key-values to 2-arrays [key, value] and then sort the array by the keys. All the process is done recursively so objects inside objects or arrays are also ordered. Once the array is created the method returns the JSON.stringify() of the sorted array.
|
|
75
|
+
*
|
|
76
|
+
* @param {object} obj the object
|
|
77
|
+
*
|
|
78
|
+
* @returns {string} a JSON stringify of the created sorted array
|
|
79
|
+
*/
|
|
80
|
+
const hashable = (obj) => {
|
|
81
|
+
return JSON.stringify(objectToArraySortedByKey(obj));
|
|
82
|
+
};
|
package/lib/index.d.ts
ADDED
package/lib/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
tslib_1.__exportStar(require("./interfaces"), exports);
|
|
5
|
+
tslib_1.__exportStar(require("./repository"), exports);
|
|
6
|
+
tslib_1.__exportStar(require("./ref"), exports);
|
|
7
|
+
tslib_1.__exportStar(require("./size"), exports);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Commit } from './commit';
|
|
2
|
+
export interface Reference {
|
|
3
|
+
name: string;
|
|
4
|
+
value: string;
|
|
5
|
+
}
|
|
6
|
+
export interface Change {
|
|
7
|
+
path: any[];
|
|
8
|
+
newValue: any | undefined;
|
|
9
|
+
oldValue: any | undefined;
|
|
10
|
+
}
|
|
11
|
+
export interface History {
|
|
12
|
+
refs: Map<string, Reference>;
|
|
13
|
+
commits: Commit[];
|
|
14
|
+
}
|
package/lib/ref.d.ts
ADDED
package/lib/ref.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validBranch = exports.validRef = void 0;
|
|
4
|
+
const bad = /(^|[/.])([/.]|$)|^@$|@{|[\x00-\x20\x7f~^:?*[\\]|\.lock(\/|$)/;
|
|
5
|
+
const badBranch = /^(-|HEAD$)/;
|
|
6
|
+
function validRef(name, onelevel) {
|
|
7
|
+
return !bad.test(name) && (onelevel || name.includes('/'));
|
|
8
|
+
}
|
|
9
|
+
exports.validRef = validRef;
|
|
10
|
+
function validBranch(name) {
|
|
11
|
+
return validRef(name, true) && !badBranch.test(name);
|
|
12
|
+
}
|
|
13
|
+
exports.validBranch = validBranch;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { History } from './interfaces';
|
|
2
|
+
import { Commit } from './commit';
|
|
3
|
+
export interface RepositoryOptions<T> {
|
|
4
|
+
history?: History;
|
|
5
|
+
}
|
|
6
|
+
export interface RepositoryObject<T> {
|
|
7
|
+
data: T;
|
|
8
|
+
getHistory(): History;
|
|
9
|
+
head(): string;
|
|
10
|
+
ref(reference: string): string | undefined;
|
|
11
|
+
commit(message: string, author: string, amend?: boolean): Promise<string>;
|
|
12
|
+
checkout(shaish: string, createBranch?: boolean): void;
|
|
13
|
+
logs(commits?: number): Commit[];
|
|
14
|
+
createBranch(name: string): string;
|
|
15
|
+
merge(source: string | RepositoryObject<T> | History): string;
|
|
16
|
+
branch(): string;
|
|
17
|
+
}
|
|
18
|
+
export interface RespositoryObjectType {
|
|
19
|
+
new <T>(obj: T, options: RepositoryOptions<T>): RepositoryObject<T>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* A repository recording and managing the state transitions of an object
|
|
23
|
+
*/
|
|
24
|
+
export declare const Repository: RespositoryObjectType;
|
|
25
|
+
/**
|
|
26
|
+
* Prints the underlying changelog of a repository
|
|
27
|
+
* @param repository
|
|
28
|
+
*/
|
|
29
|
+
export declare const printChangeLog: <T>(repository: RepositoryObject<T>) => void;
|
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.printChangeLog = exports.Repository = void 0;
|
|
4
|
+
const commit_1 = require("./commit");
|
|
5
|
+
const ref_1 = require("./ref");
|
|
6
|
+
const REFS_HEAD = 'HEAD';
|
|
7
|
+
const REFS_MAIN = 'refs/heads/main';
|
|
8
|
+
const refPrefix = 'ref: ';
|
|
9
|
+
/**
|
|
10
|
+
* A repository recording and managing the state transitions of an object
|
|
11
|
+
*/
|
|
12
|
+
exports.Repository = function (obj, options) {
|
|
13
|
+
var _a, _b, _c, _d;
|
|
14
|
+
let savedLength;
|
|
15
|
+
let version = 0;
|
|
16
|
+
const refs = (_b = (_a = options.history) === null || _a === void 0 ? void 0 : _a.refs) !== null && _b !== void 0 ? _b : new Map([[REFS_HEAD, {
|
|
17
|
+
name: REFS_HEAD,
|
|
18
|
+
value: `ref: ${REFS_MAIN}`
|
|
19
|
+
}]]);
|
|
20
|
+
const changeLog = [];
|
|
21
|
+
const targets = [];
|
|
22
|
+
const commits = (_d = (_c = options.history) === null || _c === void 0 ? void 0 : _c.commits) !== null && _d !== void 0 ? _d : [];
|
|
23
|
+
const hash = new Map([[obj, []]]);
|
|
24
|
+
const handler = {
|
|
25
|
+
get: function (target, property) {
|
|
26
|
+
var _a;
|
|
27
|
+
const x = target[property];
|
|
28
|
+
if (Object(x) !== x)
|
|
29
|
+
return x;
|
|
30
|
+
const arr = (_a = hash.get(target)) !== null && _a !== void 0 ? _a : [];
|
|
31
|
+
hash.set(x, [...arr, property]);
|
|
32
|
+
return new Proxy(x, handler);
|
|
33
|
+
},
|
|
34
|
+
set: update,
|
|
35
|
+
deleteProperty: update
|
|
36
|
+
};
|
|
37
|
+
function gotoVersion(newVersion) {
|
|
38
|
+
newVersion = Math.max(0, Math.min(changeLog.length, newVersion));
|
|
39
|
+
let chg;
|
|
40
|
+
let target;
|
|
41
|
+
let path;
|
|
42
|
+
let property;
|
|
43
|
+
const val = newVersion > version ? 'newValue' : 'oldValue';
|
|
44
|
+
while (version !== newVersion) {
|
|
45
|
+
if (version > newVersion)
|
|
46
|
+
version--;
|
|
47
|
+
chg = changeLog[version];
|
|
48
|
+
path = [...chg.path];
|
|
49
|
+
property = path.pop();
|
|
50
|
+
target =
|
|
51
|
+
targets[version] ||
|
|
52
|
+
(targets[version] = path.reduce((o, p) => o[p], obj));
|
|
53
|
+
if (chg.hasOwnProperty(val)) {
|
|
54
|
+
const oldValue = chg[val];
|
|
55
|
+
// Some additional care concerning the length property of arrays:
|
|
56
|
+
// @nadilas workaround: array trim to empty array should not set 0:undefined
|
|
57
|
+
// console.log('warn: not setting array[0]=undefined', target, property, oldValue)
|
|
58
|
+
if (!(Array.isArray(target) &&
|
|
59
|
+
target.length === 0 &&
|
|
60
|
+
oldValue === undefined)) {
|
|
61
|
+
target[property] = oldValue;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
delete target[property];
|
|
66
|
+
}
|
|
67
|
+
if (version < newVersion) {
|
|
68
|
+
version++;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
function gotoLastVersion() {
|
|
74
|
+
return gotoVersion(changeLog.length);
|
|
75
|
+
}
|
|
76
|
+
function update(target, property, value) {
|
|
77
|
+
var _a;
|
|
78
|
+
// only last version can be modified
|
|
79
|
+
gotoLastVersion();
|
|
80
|
+
const changes = (_a = hash.get(target)) !== null && _a !== void 0 ? _a : [];
|
|
81
|
+
const change = {
|
|
82
|
+
path: [...changes, property],
|
|
83
|
+
newValue: undefined,
|
|
84
|
+
oldValue: undefined
|
|
85
|
+
};
|
|
86
|
+
if (arguments.length > 2)
|
|
87
|
+
change.newValue = value;
|
|
88
|
+
// Some care concerning the length property of arrays:
|
|
89
|
+
if (Array.isArray(target) && Number(property) >= target.length) {
|
|
90
|
+
savedLength = target.length;
|
|
91
|
+
}
|
|
92
|
+
if (property in target) {
|
|
93
|
+
if (property === 'length' && savedLength !== undefined) {
|
|
94
|
+
change.oldValue = savedLength;
|
|
95
|
+
savedLength = undefined;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
change.oldValue = target[property];
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
changeLog.push(change);
|
|
102
|
+
targets.push(target);
|
|
103
|
+
return gotoLastVersion();
|
|
104
|
+
}
|
|
105
|
+
this.data = new Proxy(obj, handler);
|
|
106
|
+
// region Read state read
|
|
107
|
+
this.head = () => {
|
|
108
|
+
const ref = refs.get(REFS_HEAD);
|
|
109
|
+
if (!ref) {
|
|
110
|
+
throw new Error(`unreachable: HEAD is not present`);
|
|
111
|
+
}
|
|
112
|
+
return cleanRefValue(ref.value);
|
|
113
|
+
};
|
|
114
|
+
this.ref = (reference) => {
|
|
115
|
+
var _a;
|
|
116
|
+
const ref = (_a = refs.get(reference)) === null || _a === void 0 ? void 0 : _a.value;
|
|
117
|
+
return ref ? cleanRefValue(ref) : undefined;
|
|
118
|
+
};
|
|
119
|
+
this.branch = () => {
|
|
120
|
+
const currentHeadRef = refs.get(REFS_HEAD);
|
|
121
|
+
if (!currentHeadRef) {
|
|
122
|
+
throw new Error('unreachable: ref HEAD not available');
|
|
123
|
+
}
|
|
124
|
+
if (currentHeadRef.value.includes(refPrefix)) {
|
|
125
|
+
const refName = cleanRefValue(currentHeadRef.value);
|
|
126
|
+
if (refs.has(refName))
|
|
127
|
+
return getLastItem(refName);
|
|
128
|
+
}
|
|
129
|
+
return REFS_HEAD; // detached state
|
|
130
|
+
};
|
|
131
|
+
// endregion
|
|
132
|
+
// region History functions
|
|
133
|
+
const collectCommits = () => {
|
|
134
|
+
const commit = commitAtHead();
|
|
135
|
+
if (!commit) {
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
// traverse backwards and build commit tree
|
|
139
|
+
let c = commit;
|
|
140
|
+
let commitsList = [];
|
|
141
|
+
while (c !== undefined) {
|
|
142
|
+
commitsList = [c, ...commitsList];
|
|
143
|
+
c = commits.find(parent => parent.hash === (c === null || c === void 0 ? void 0 : c.parent));
|
|
144
|
+
}
|
|
145
|
+
return commitsList;
|
|
146
|
+
};
|
|
147
|
+
const rebuildChangeLog = (commit) => {
|
|
148
|
+
// clear current state
|
|
149
|
+
changeLog.splice(0);
|
|
150
|
+
version = 0;
|
|
151
|
+
// traverse backwards and build changelog
|
|
152
|
+
let clog = traverseAndCollectChangelog(commit, commits);
|
|
153
|
+
changeLog.push(...clog);
|
|
154
|
+
// process new changelog
|
|
155
|
+
gotoLastVersion();
|
|
156
|
+
};
|
|
157
|
+
this.logs = (numberOfCommits) => {
|
|
158
|
+
const logs = [];
|
|
159
|
+
const limit = numberOfCommits !== null && numberOfCommits !== void 0 ? numberOfCommits : -1;
|
|
160
|
+
let c = commitAtHead();
|
|
161
|
+
let counter = 0;
|
|
162
|
+
while (c !== undefined && (limit === -1 || counter < limit)) {
|
|
163
|
+
logs.push(c, ...logs);
|
|
164
|
+
counter++;
|
|
165
|
+
c = commits.find(parent => parent.hash === (c === null || c === void 0 ? void 0 : c.parent));
|
|
166
|
+
}
|
|
167
|
+
return logs;
|
|
168
|
+
};
|
|
169
|
+
// endregion
|
|
170
|
+
this.getHistory = () => {
|
|
171
|
+
// only send back shallow copies of changelog and commits up to current version
|
|
172
|
+
return {
|
|
173
|
+
refs: new Map(refs),
|
|
174
|
+
commits: collectCommits()
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
// region Commit lookups
|
|
178
|
+
const commitAtHead = () => {
|
|
179
|
+
return commitAtHeadIn(REFS_HEAD, refs, commits);
|
|
180
|
+
};
|
|
181
|
+
const mustCommitAtHead = () => {
|
|
182
|
+
const commitHead = commitAtHead();
|
|
183
|
+
if (!commitHead) {
|
|
184
|
+
throw new Error(`unreachable: HEAD or its target ref not present`);
|
|
185
|
+
}
|
|
186
|
+
return commitHead;
|
|
187
|
+
};
|
|
188
|
+
// endregion
|
|
189
|
+
this.commit = async (message, author, amend = false) => {
|
|
190
|
+
let parent = commitAtHead();
|
|
191
|
+
if (amend && !parent) {
|
|
192
|
+
throw new Error(`no commit to amend`);
|
|
193
|
+
}
|
|
194
|
+
if (parent) {
|
|
195
|
+
if (amend) {
|
|
196
|
+
[parent] = parent.parent ? shaishToCommit(parent.parent, refs, commits) : [undefined];
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const changesSinceLastCommit = changeLog.slice(parent === null || parent === void 0 ? void 0 : parent.to);
|
|
200
|
+
if (changesSinceLastCommit.length === 0) {
|
|
201
|
+
throw new Error(`no changes to commit`);
|
|
202
|
+
}
|
|
203
|
+
const timestamp = new Date();
|
|
204
|
+
const changes = [...changesSinceLastCommit];
|
|
205
|
+
const sha = await (0, commit_1.calculateHash)({
|
|
206
|
+
message,
|
|
207
|
+
author,
|
|
208
|
+
changes,
|
|
209
|
+
parentRef: parent === null || parent === void 0 ? void 0 : parent.hash,
|
|
210
|
+
timestamp
|
|
211
|
+
});
|
|
212
|
+
const commit = {
|
|
213
|
+
hash: sha,
|
|
214
|
+
message,
|
|
215
|
+
author,
|
|
216
|
+
changes: changes,
|
|
217
|
+
parent: parent === null || parent === void 0 ? void 0 : parent.hash,
|
|
218
|
+
timestamp,
|
|
219
|
+
to: version
|
|
220
|
+
};
|
|
221
|
+
if (amend) {
|
|
222
|
+
const idx = commits.findIndex(c => c === parent);
|
|
223
|
+
commits.splice(idx, 1);
|
|
224
|
+
}
|
|
225
|
+
commits.push(commit);
|
|
226
|
+
const headRef = this.head();
|
|
227
|
+
if (headRef.includes('refs')) {
|
|
228
|
+
// but move ref: refs/heads/main
|
|
229
|
+
moveRef(headRef, commit);
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
// move detached HEAD to new commit
|
|
233
|
+
moveRef(REFS_HEAD, commit);
|
|
234
|
+
}
|
|
235
|
+
return sha;
|
|
236
|
+
};
|
|
237
|
+
// region Graph manipulation
|
|
238
|
+
this.checkout = (shaish, createBranch = false) => {
|
|
239
|
+
if (createBranch) {
|
|
240
|
+
validateBranchName(shaish);
|
|
241
|
+
let branchRef = brancheNameToRef(shaish);
|
|
242
|
+
const commit = commitAtHead();
|
|
243
|
+
if (commit) {
|
|
244
|
+
moveRef(branchRef, commit);
|
|
245
|
+
}
|
|
246
|
+
moveRef(REFS_HEAD, branchRef);
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
const [commit, isRef, refKey] = shaishToCommit(shaish, refs, commits);
|
|
250
|
+
rebuildChangeLog(commit);
|
|
251
|
+
moveRef(REFS_HEAD, isRef && refKey !== undefined ? refKey : commit);
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
this.createBranch = (name) => {
|
|
255
|
+
validateBranchName(name);
|
|
256
|
+
const refName = brancheNameToRef(name);
|
|
257
|
+
const headCommit = commitAtHead();
|
|
258
|
+
if (!headCommit) {
|
|
259
|
+
const headRef = this.head();
|
|
260
|
+
if (!headRef) {
|
|
261
|
+
throw new Error(`unreachable: HEAD not present`);
|
|
262
|
+
}
|
|
263
|
+
throw new Error(`fatal: not a valid object name: '${getLastItem(headRef)}'`);
|
|
264
|
+
}
|
|
265
|
+
refs.set(refName, { name: name, value: headCommit.hash });
|
|
266
|
+
return refName;
|
|
267
|
+
};
|
|
268
|
+
const moveRef = (refName, value) => {
|
|
269
|
+
let ref = refs.get(refName);
|
|
270
|
+
const val = typeof value === 'string' ? `${refPrefix}${value}` : value.hash;
|
|
271
|
+
if (!ref) {
|
|
272
|
+
ref = { name: getLastItem(refName), value: val };
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
ref.value = val;
|
|
276
|
+
}
|
|
277
|
+
refs.set(refName, ref);
|
|
278
|
+
};
|
|
279
|
+
this.merge = source => {
|
|
280
|
+
// inspiration
|
|
281
|
+
// http://think-like-a-git.net
|
|
282
|
+
// also check isomorphic-git
|
|
283
|
+
// for fancier merge tree
|
|
284
|
+
// https://github.com/isomorphic-git/isomorphic-git/blob/a623133345a5d8b6bb7a8352ea9702ce425d8266/src/utils/mergeTree.js#L33
|
|
285
|
+
if (typeof source !== 'string') {
|
|
286
|
+
// const srcHead = commitAtHeadIn(REFS_HEAD, src.refs, src.commits)
|
|
287
|
+
throw new Error(`fatal: source type (${source instanceof exports.Repository ? 'Repository' : 'History'}) not implemented`);
|
|
288
|
+
}
|
|
289
|
+
const [srcCommit] = shaishToCommit(source, refs, commits);
|
|
290
|
+
const headCommit = mustCommitAtHead();
|
|
291
|
+
// no change
|
|
292
|
+
// *---* (master)
|
|
293
|
+
// |
|
|
294
|
+
// * (foo)
|
|
295
|
+
if (headCommit.hash === srcCommit.hash) {
|
|
296
|
+
throw new Error(`already up to date`);
|
|
297
|
+
}
|
|
298
|
+
// fast-forward
|
|
299
|
+
// *---* (master)
|
|
300
|
+
// \
|
|
301
|
+
// *---*---* (foo)
|
|
302
|
+
// result:
|
|
303
|
+
// *---*
|
|
304
|
+
// \
|
|
305
|
+
// *---*---* (master, foo)
|
|
306
|
+
const [isAncestor] = mapPath(headCommit, srcCommit, commits);
|
|
307
|
+
if (isAncestor) {
|
|
308
|
+
moveRef(this.head(), srcCommit);
|
|
309
|
+
rebuildChangeLog(srcCommit);
|
|
310
|
+
return srcCommit.hash;
|
|
311
|
+
}
|
|
312
|
+
// todo diverge
|
|
313
|
+
// *---*---* (master)
|
|
314
|
+
// \
|
|
315
|
+
// *---*---* (foo)
|
|
316
|
+
// result:
|
|
317
|
+
// ↓
|
|
318
|
+
// *---*---*-------* (master)
|
|
319
|
+
// \ /
|
|
320
|
+
// *---*---* (foo)
|
|
321
|
+
// if (false) {
|
|
322
|
+
// throw new Error('diverge not implemented yet')
|
|
323
|
+
// }
|
|
324
|
+
throw new Error('unknown merge type: not implemented yet');
|
|
325
|
+
};
|
|
326
|
+
// endregion
|
|
327
|
+
// apply change log at the end of the constructor
|
|
328
|
+
const headCommit = commitAtHead();
|
|
329
|
+
if (headCommit) {
|
|
330
|
+
rebuildChangeLog(headCommit);
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
const getLastItem = (thePath) => thePath.substring(thePath.lastIndexOf('/') + 1);
|
|
334
|
+
const cleanRefValue = (ref) => ref.replace(refPrefix, '');
|
|
335
|
+
const brancheNameToRef = (name) => {
|
|
336
|
+
return `refs/heads/${name}`;
|
|
337
|
+
};
|
|
338
|
+
const validateBranchName = (name) => {
|
|
339
|
+
if (!(0, ref_1.validBranch)(name)) {
|
|
340
|
+
throw new Error(`invalid ref name`);
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
/**
|
|
344
|
+
* Traverses the commit tree backwards and reassembles the changelog
|
|
345
|
+
* @param commit
|
|
346
|
+
* @param commitsList
|
|
347
|
+
*/
|
|
348
|
+
const traverseAndCollectChangelog = (commit, commitsList) => {
|
|
349
|
+
let c = commit;
|
|
350
|
+
let clog = [];
|
|
351
|
+
while (c !== undefined) {
|
|
352
|
+
clog = [...commit.changes, ...clog];
|
|
353
|
+
c = commitsList.find(parent => parent.hash === (c === null || c === void 0 ? void 0 : c.parent));
|
|
354
|
+
}
|
|
355
|
+
return clog;
|
|
356
|
+
};
|
|
357
|
+
const mapPath = (from, to, commits) => {
|
|
358
|
+
let c = to;
|
|
359
|
+
while (c !== undefined) {
|
|
360
|
+
c = commits.find(parent => parent.hash === (c === null || c === void 0 ? void 0 : c.parent));
|
|
361
|
+
if ((c === null || c === void 0 ? void 0 : c.hash) === from.hash) {
|
|
362
|
+
return [true];
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return [false];
|
|
366
|
+
};
|
|
367
|
+
/**
|
|
368
|
+
* Returns the commit to which the provided ref is pointing
|
|
369
|
+
* @param ref - needs to be in key format, e.g. refs/heads/... or refs/tags/...
|
|
370
|
+
* @param references
|
|
371
|
+
* @param commitsList
|
|
372
|
+
*/
|
|
373
|
+
const commitAtHeadIn = (ref, references, commitsList) => {
|
|
374
|
+
const reference = references.get(ref);
|
|
375
|
+
if (!reference) {
|
|
376
|
+
throw new Error(`unreachable: '${ref}' is not present`);
|
|
377
|
+
}
|
|
378
|
+
let commitHash;
|
|
379
|
+
if (reference.value.includes(refPrefix)) {
|
|
380
|
+
const refKey = cleanRefValue(reference.value);
|
|
381
|
+
const targetRef = references.get(refKey);
|
|
382
|
+
if (!targetRef) {
|
|
383
|
+
// target branch may not have been saved yet
|
|
384
|
+
return undefined;
|
|
385
|
+
}
|
|
386
|
+
commitHash = targetRef.value;
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
commitHash = reference.value;
|
|
390
|
+
}
|
|
391
|
+
for (const c of commitsList) {
|
|
392
|
+
if (c.hash === commitHash) {
|
|
393
|
+
return c;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return undefined;
|
|
397
|
+
};
|
|
398
|
+
/**
|
|
399
|
+
* Accepts a shaish expression (e.g. refs (branches, tags), commitSha) and returns
|
|
400
|
+
* - a commit of type Commit
|
|
401
|
+
* - isRef boolean whether it is a direct reference
|
|
402
|
+
* - ref the key of the reference
|
|
403
|
+
*/
|
|
404
|
+
const shaishToCommit = (shaish, references, commitsList) => {
|
|
405
|
+
let sha = shaish;
|
|
406
|
+
let isRef = false;
|
|
407
|
+
let refKey = undefined;
|
|
408
|
+
// check for refs
|
|
409
|
+
for (const [name, ref] of references.entries()) {
|
|
410
|
+
// match on
|
|
411
|
+
if (ref.name === shaish || name === shaish) {
|
|
412
|
+
isRef = true;
|
|
413
|
+
refKey = name;
|
|
414
|
+
sha = ref.value;
|
|
415
|
+
if (sha.includes(refPrefix)) {
|
|
416
|
+
const cleanedRef = cleanRefValue(sha);
|
|
417
|
+
const c = commitAtHeadIn(cleanedRef, references, commitsList);
|
|
418
|
+
if (!c) {
|
|
419
|
+
throw new Error(`${cleanedRef} points to non-existing commit`);
|
|
420
|
+
}
|
|
421
|
+
return [c, isRef, refKey];
|
|
422
|
+
}
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
// check for partial sha matches
|
|
427
|
+
const found = commitsList.filter(c => c.hash.indexOf(sha) > -1);
|
|
428
|
+
if (found.length === 0) {
|
|
429
|
+
throw new Error(`pathspec '${shaish}' did not match any known refs`);
|
|
430
|
+
}
|
|
431
|
+
// but sha should be specific enough to resolve to 1 commit
|
|
432
|
+
if (found.length > 1) {
|
|
433
|
+
throw new Error(`commit `);
|
|
434
|
+
}
|
|
435
|
+
return [found[0], isRef, refKey];
|
|
436
|
+
};
|
|
437
|
+
/**
|
|
438
|
+
* Prints the underlying changelog of a repository
|
|
439
|
+
* @param repository
|
|
440
|
+
*/
|
|
441
|
+
const printChangeLog = (repository) => {
|
|
442
|
+
console.log('----------------------------------------------------------');
|
|
443
|
+
console.log(`Changelog at ${repository.head()}`);
|
|
444
|
+
const history = repository.getHistory();
|
|
445
|
+
const head = commitAtHeadIn(repository.head(), history.refs, history.commits);
|
|
446
|
+
if (!head) {
|
|
447
|
+
throw new Error(`fatal: HEAD is not defined`);
|
|
448
|
+
}
|
|
449
|
+
const changeLog = traverseAndCollectChangelog(head, history.commits);
|
|
450
|
+
for (const [, chg] of changeLog.entries()) {
|
|
451
|
+
console.log(` ${JSON.stringify(chg)}`);
|
|
452
|
+
}
|
|
453
|
+
console.log('----------------------------------------------------------');
|
|
454
|
+
};
|
|
455
|
+
exports.printChangeLog = printChangeLog;
|
package/lib/size.d.ts
ADDED
package/lib/size.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sizeInMegabytes = exports.sizeInKilobytes = exports.sizeInBytes = exports.getSizeInBytes = void 0;
|
|
4
|
+
const getSizeInBytes = (obj) => {
|
|
5
|
+
let str;
|
|
6
|
+
if (typeof obj === 'string') {
|
|
7
|
+
// If obj is a string, then use it
|
|
8
|
+
str = obj;
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
// Else, make obj into a string
|
|
12
|
+
str = JSON.stringify(obj);
|
|
13
|
+
}
|
|
14
|
+
// Get the length of the Uint8Array
|
|
15
|
+
return new TextEncoder().encode(str).length; // in bytes
|
|
16
|
+
};
|
|
17
|
+
exports.getSizeInBytes = getSizeInBytes;
|
|
18
|
+
const sizeInBytes = (obj) => {
|
|
19
|
+
return (0, exports.getSizeInBytes)(obj); // B
|
|
20
|
+
};
|
|
21
|
+
exports.sizeInBytes = sizeInBytes;
|
|
22
|
+
const sizeInKilobytes = (obj) => {
|
|
23
|
+
const bytes = (0, exports.getSizeInBytes)(obj);
|
|
24
|
+
return (bytes / 1000); // kB
|
|
25
|
+
};
|
|
26
|
+
exports.sizeInKilobytes = sizeInKilobytes;
|
|
27
|
+
const sizeInMegabytes = (obj) => {
|
|
28
|
+
const bytes = (0, exports.getSizeInBytes)(obj);
|
|
29
|
+
return (bytes / 1000 / 1000); // mB
|
|
30
|
+
};
|
|
31
|
+
exports.sizeInMegabytes = sizeInMegabytes;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { RepositoryObject } from './repository';
|
|
2
|
+
import { Commit } from './commit';
|
|
3
|
+
export declare class NestedObject {
|
|
4
|
+
name: string | undefined;
|
|
5
|
+
uuid: string | undefined;
|
|
6
|
+
}
|
|
7
|
+
export declare class ComplexObject {
|
|
8
|
+
uuid: string | undefined;
|
|
9
|
+
name: string | undefined;
|
|
10
|
+
description: string | undefined;
|
|
11
|
+
nested: NestedObject[];
|
|
12
|
+
}
|
|
13
|
+
export declare const testAuthor = "User name <name@domain.com>";
|
|
14
|
+
export declare function getBaseline(): Promise<[RepositoryObject<ComplexObject>, ComplexObject]>;
|
|
15
|
+
export declare function updateHeaderData(wrapped: ComplexObject): number;
|
|
16
|
+
export declare function addOneStep(wrapped: ComplexObject): number;
|
|
17
|
+
export declare function sumChanges(commits: Commit[] | undefined): number | undefined;
|