mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-10-13 02:59:36 +02:00
269 lines
6.4 KiB
JavaScript
269 lines
6.4 KiB
JavaScript
import { log } from '../../logger';
|
|
import { random } from '../../utils';
|
|
let commits = {};
|
|
let head = null;
|
|
let branches = { master: head };
|
|
let curBranch = 'master';
|
|
let direction = 'LR';
|
|
let seq = 0;
|
|
|
|
function getId() {
|
|
return random({ length: 7 });
|
|
}
|
|
|
|
/**
|
|
* @param currentCommit
|
|
* @param otherCommit
|
|
*/
|
|
function isfastforwardable(currentCommit, otherCommit) {
|
|
log.debug('Entering isfastforwardable:', currentCommit.id, otherCommit.id);
|
|
let cnt = 0;
|
|
while (currentCommit.seq <= otherCommit.seq && currentCommit !== otherCommit && cnt < 1000) {
|
|
cnt++;
|
|
// only if other branch has more commits
|
|
if (otherCommit.parent == null) break;
|
|
if (Array.isArray(otherCommit.parent)) {
|
|
log.debug('In merge commit:', otherCommit.parent);
|
|
return (
|
|
isfastforwardable(currentCommit, commits[otherCommit.parent[0]]) ||
|
|
isfastforwardable(currentCommit, commits[otherCommit.parent[1]])
|
|
);
|
|
} else {
|
|
otherCommit = commits[otherCommit.parent];
|
|
}
|
|
}
|
|
log.debug(currentCommit.id, otherCommit.id);
|
|
return currentCommit.id === otherCommit.id;
|
|
}
|
|
|
|
/**
|
|
* @param currentCommit
|
|
* @param otherCommit
|
|
*/
|
|
function isReachableFrom(currentCommit, otherCommit) {
|
|
const currentSeq = currentCommit.seq;
|
|
const otherSeq = otherCommit.seq;
|
|
if (currentSeq > otherSeq) return isfastforwardable(otherCommit, currentCommit);
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @param list
|
|
* @param fn
|
|
*/
|
|
function uniqBy(list, fn) {
|
|
const recordMap = Object.create(null);
|
|
return list.reduce((out, item) => {
|
|
const key = fn(item);
|
|
if (!recordMap[key]) {
|
|
recordMap[key] = true;
|
|
out.push(item);
|
|
}
|
|
return out;
|
|
}, []);
|
|
}
|
|
|
|
export const setDirection = function (dir) {
|
|
direction = dir;
|
|
};
|
|
let options = {};
|
|
export const setOptions = function (rawOptString) {
|
|
log.debug('options str', rawOptString);
|
|
rawOptString = rawOptString && rawOptString.trim();
|
|
rawOptString = rawOptString || '{}';
|
|
try {
|
|
options = JSON.parse(rawOptString);
|
|
} catch (e) {
|
|
log.error('error while parsing gitGraph options', e.message);
|
|
}
|
|
};
|
|
|
|
export const getOptions = function () {
|
|
return options;
|
|
};
|
|
|
|
export const commit = function (msg) {
|
|
const commit = {
|
|
id: getId(),
|
|
message: msg,
|
|
seq: seq++,
|
|
parent: head == null ? null : head.id,
|
|
};
|
|
head = commit;
|
|
commits[commit.id] = commit;
|
|
branches[curBranch] = commit.id;
|
|
log.debug('in pushCommit ' + commit.id);
|
|
};
|
|
|
|
export const branch = function (name) {
|
|
branches[name] = head != null ? head.id : null;
|
|
log.debug('in createBranch');
|
|
};
|
|
|
|
export const merge = function (otherBranch) {
|
|
const currentCommit = commits[branches[curBranch]];
|
|
const otherCommit = commits[branches[otherBranch]];
|
|
if (isReachableFrom(currentCommit, otherCommit)) {
|
|
log.debug('Already merged');
|
|
return;
|
|
}
|
|
if (isfastforwardable(currentCommit, otherCommit)) {
|
|
branches[curBranch] = branches[otherBranch];
|
|
head = commits[branches[curBranch]];
|
|
} else {
|
|
// create merge commit
|
|
const commit = {
|
|
id: getId(),
|
|
message: 'merged branch ' + otherBranch + ' into ' + curBranch,
|
|
seq: seq++,
|
|
parent: [head == null ? null : head.id, branches[otherBranch]],
|
|
};
|
|
head = commit;
|
|
commits[commit.id] = commit;
|
|
branches[curBranch] = commit.id;
|
|
}
|
|
log.debug(branches);
|
|
log.debug('in mergeBranch');
|
|
};
|
|
|
|
export const checkout = function (branch) {
|
|
log.debug('in checkout');
|
|
curBranch = branch;
|
|
const id = branches[curBranch];
|
|
head = commits[id];
|
|
};
|
|
|
|
export const reset = function (commitRef) {
|
|
log.debug('in reset', commitRef);
|
|
const ref = commitRef.split(':')[0];
|
|
let parentCount = parseInt(commitRef.split(':')[1]);
|
|
let commit = ref === 'HEAD' ? head : commits[branches[ref]];
|
|
log.debug(commit, parentCount);
|
|
while (parentCount > 0) {
|
|
commit = commits[commit.parent];
|
|
parentCount--;
|
|
if (!commit) {
|
|
const err = 'Critical error - unique parent commit not found during reset';
|
|
log.error(err);
|
|
throw err;
|
|
}
|
|
}
|
|
head = commit;
|
|
branches[curBranch] = commit.id;
|
|
};
|
|
|
|
/**
|
|
* @param arr
|
|
* @param key
|
|
* @param newval
|
|
*/
|
|
function upsert(arr, key, newval) {
|
|
const index = arr.indexOf(key);
|
|
if (index === -1) {
|
|
arr.push(newval);
|
|
} else {
|
|
arr.splice(index, 1, newval);
|
|
}
|
|
}
|
|
|
|
/** @param commitArr */
|
|
function prettyPrintCommitHistory(commitArr) {
|
|
const commit = commitArr.reduce((out, commit) => {
|
|
if (out.seq > commit.seq) return out;
|
|
return commit;
|
|
}, commitArr[0]);
|
|
let line = '';
|
|
commitArr.forEach(function (c) {
|
|
if (c === commit) {
|
|
line += '\t*';
|
|
} else {
|
|
line += '\t|';
|
|
}
|
|
});
|
|
const label = [line, commit.id, commit.seq];
|
|
for (let branch in branches) {
|
|
if (branches[branch] === commit.id) label.push(branch);
|
|
}
|
|
log.debug(label.join(' '));
|
|
if (Array.isArray(commit.parent)) {
|
|
const newCommit = commits[commit.parent[0]];
|
|
upsert(commitArr, commit, newCommit);
|
|
commitArr.push(commits[commit.parent[1]]);
|
|
} else if (commit.parent == null) {
|
|
return;
|
|
} else {
|
|
const nextCommit = commits[commit.parent];
|
|
upsert(commitArr, commit, nextCommit);
|
|
}
|
|
commitArr = uniqBy(commitArr, (c) => c.id);
|
|
prettyPrintCommitHistory(commitArr);
|
|
}
|
|
|
|
export const prettyPrint = function () {
|
|
log.debug(commits);
|
|
const node = getCommitsArray()[0];
|
|
prettyPrintCommitHistory([node]);
|
|
};
|
|
|
|
export const clear = function () {
|
|
commits = {};
|
|
head = null;
|
|
branches = { master: head };
|
|
curBranch = 'master';
|
|
seq = 0;
|
|
};
|
|
|
|
export const getBranchesAsObjArray = function () {
|
|
const branchArr = [];
|
|
for (let branch in branches) {
|
|
branchArr.push({ name: branch, commit: commits[branches[branch]] });
|
|
}
|
|
return branchArr;
|
|
};
|
|
|
|
export const getBranches = function () {
|
|
return branches;
|
|
};
|
|
export const getCommits = function () {
|
|
return commits;
|
|
};
|
|
export const getCommitsArray = function () {
|
|
const commitArr = Object.keys(commits).map(function (key) {
|
|
return commits[key];
|
|
});
|
|
commitArr.forEach(function (o) {
|
|
log.debug(o.id);
|
|
});
|
|
commitArr.sort((a, b) => b.seq - a.seq);
|
|
return commitArr;
|
|
};
|
|
export const getCurrentBranch = function () {
|
|
return curBranch;
|
|
};
|
|
export const getDirection = function () {
|
|
return direction;
|
|
};
|
|
export const getHead = function () {
|
|
return head;
|
|
};
|
|
|
|
export default {
|
|
setDirection,
|
|
setOptions,
|
|
getOptions,
|
|
commit,
|
|
branch,
|
|
merge,
|
|
checkout,
|
|
reset,
|
|
prettyPrint,
|
|
clear,
|
|
getBranchesAsObjArray,
|
|
getBranches,
|
|
getCommits,
|
|
getCommitsArray,
|
|
getCurrentBranch,
|
|
getDirection,
|
|
getHead,
|
|
};
|