UNPKG

thegit

Version:

Implementation of git in node.js.

262 lines (239 loc) 6.91 kB
const objectHash = require("object-hash"); const globals = require("./../globals"); const zlib = require("node:zlib"); const { createEmptyFile, deleteFile, deleteDir, createDir, listPathsInDir, writeToFile, readFile, pathExists, isFile, isDir } = require('./fs_util'); function isInit() { for (let dir of globals.baseDirs) { if (!pathExists(dir)) { console.log(dir + " is missing"); return false; } } for (let file of globals.baseFiles) { if (!pathExists(file)) { console.log(file + " is missing"); return false; } } return true; } function createObjectFromFileContent(fileContent) { let fileHash = hash(fileContent); createDir(globals.objectDir + fileHash.slice(0, 2)); let filePath = `${globals.objectDir}${fileHash.slice(0, 2)}/${fileHash.slice( 2 )}`; createEmptyFile(filePath); let compressedFile = compress(fileContent); writeToFile(filePath, compressedFile, "utf-8"); } function debug(...inp) { console.log(...inp); } function logMessage(msg) { console.log(msg); } function setRootDir(path) { globals.rootDir = path; } function hash(data, algorithm = "sha1") { return objectHash(data, { algorithm: algorithm, }); } function compress(data) { return data; // return zlib.deflateSync(data); } function decompress(data) { return data.toString(); // let ret = zlib.inflateSync(data).toString(); // return ret; } function createCommitStr(treeHash,mergeParent = null) { let commitStr = ""; let time = new Date(); time = time.getTime(); commitStr += `tree ${treeHash}\n`; if(mergeParent == null){ let parentCommit = getLastCommit(); if (parentCommit) commitStr += `parent ${parentCommit}\n`; } else{ commitStr += `parent ${mergeParent.currCommit}\n`; commitStr += `parent ${mergeParent.incomingCommit}\n`; } commitStr += `author ${globals.username} <${globals.email}> ${time}\n`; commitStr += `committer ${globals.username} <${globals.email}> ${time}\n`; commitStr += `\n`; commitStr += `${globals.commitMessage}\n`; return commitStr; } function updateRefsWithCommit(commitStr) { let head = readFile(".legit/HEAD"); let branch = ""; if (head.split(" ")[0] != "ref:") throw "can't commit in detached state"; branch = head.split(" ")[1]; branch = ".legit/" + branch; if (!(pathExists(branch) && isFile(branch))) createEmptyFile(branch); writeToFile(branch, hash(commitStr)); } function getObjectFromHash(objectHash) { let filePath = `${globals.objectDir}${objectHash.slice( 0, 2 )}/${objectHash.slice(2)}`; return decompress(readFile(filePath, null)); } function getLastCommit() { let head = readFile(".legit/HEAD"); let branch = ""; if (head.split(" ")[0] != "ref:") return head; branch = head.split(" ")[1]; branch = ".legit/" + branch; if (!(pathExists(branch) && isFile(branch))) return false; let commit = readFile(branch).split("\n")[0]; if (commit.length == 0) return false; return commit; } function formatTimestamp(timestamp) { const date = new Date(timestamp); const options = { weekday: "short", year: "numeric", month: "short", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit", timeZoneName: "short", timeZoneOffset: "numeric", }; const formattedDate = date.toLocaleString("en-US", options); return formattedDate; } function getBranches() { return listPathsInDir(globals.headsDir.slice(0, -1)).filter( (path) => { return isFile(globals.headsDir + path); } ); } function fullCommitHash(partialHash) { // limit of characters needed: 3 let hashDir = globals.objectDir + partialHash.slice(0, 2); let restOfHash = partialHash.slice(2); if (pathExists(hashDir)) { let objects = listPathsInDir(hashDir); let matches = objects.filter((obj) => { return obj.slice(0, restOfHash.length) == restOfHash; }); if (matches.length == 0) { return ""; } else if (matches.length > 1) { console.log("ambiguous commit hash, provide longer hash"); return ""; } else { return partialHash.slice(0, 2) + matches[0]; } } else return ""; } function getCurrentBranch() { let head = readFile(".legit/HEAD").split("\n")[0].split(" "); if (head.length == 1) return false; let ref = head.slice(-1)[0]; return ref.split("/").slice(2).join("/"); } function parseCommit(commitHash) { let file = getObjectFromHash(commitHash).split("\n"); let commitDetails = {}; for(let line = 0; line<file.length; line++){ if(file[line]=='') { commitDetails['message'] = file.slice(line+1,-1).join('\n'); break; } let key = file[line].split(" ")[0]; let value = file[line].split(" ")[1]; if(key == "author"){ commitDetails["time"] = file[line].split(" ")[3]; commitDetails['email'] = file[line].split(" ")[2].slice(1,-1); } if(key=="parent" && commitDetails.parent != undefined){ commitDetails['merge'] = true; commitDetails['incoming'] = value; commitDetails['current'] = commitDetails['parent']; } commitDetails[key]=value; } return commitDetails; } function setUpGlobals() { // set up root Dir let currDir = process.cwd().replace(/\\/g, "/").split("/"); let parentsDepth = currDir.length - 1; let rootDir = "./"; let fromLast = 0; while ( parentsDepth!=0 && !(pathExists(rootDir + ".legit") && isDir(rootDir + ".legit")) ) { parentsDepth--; rootDir += "../"; fromLast++; } if (parentsDepth <= 0) { console.log("git repository not initialised"); process.exit(); } globals.rootDir = rootDir; if (fromLast != 0) globals.rootToWorkingPath = currDir.slice(-fromLast).join("/") + "/"; // setup file command globals.commitFileCommand = `code -w ${globals.rootDir + ".legit/COMMIT_EDITMSG"}`; // set up config let configLines = readFile(".legit/config").split("\n"); configLines.forEach((line) => { if (line.split(" ")[0] == "name") { globals.username = line.split(" ")[1]; } if (line.split(" ")[0] == "email") { globals.email = line.split(" ")[1]; } }); } function isDetached() { let head = readFile(".legit/HEAD"); return head.split(" ")[0] != "ref:"; } module.exports = { isInit, createObjectFromFileContent, debug, logMessage, setRootDir, hash, compress, decompress, createCommitStr, getObjectFromHash, getLastCommit, updateRefsWithCommit, formatTimestamp, getBranches, fullCommitHash, getCurrentBranch, parseCommit, setUpGlobals, isDetached };