UNPKG

tiny-commit-walker

Version:
183 lines 6.84 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const fs = require("fs"); const promisify = require("util.promisify"); const zlib = require("zlib"); const path = require("path"); const readFileAsync = promisify(fs.readFile); const inflateAsync = promisify(zlib.inflate); class Commit { constructor(gitDir, hash, data, _packs) { this.gitDir = gitDir; this.hash = hash; this.data = data; this._packs = _packs; const [head, ...rest] = data.split('\u0000'); const [type, size] = head.split(/\s/); this.size = +size; this.body = rest.join('\u0000'); // parse parent hashes const m = this.body.match(/^parent\s[a-f0-9]{40}/gm); this.parentHashes = m ? m.map(s => s.split(/\s/).pop()) : []; this.message = this.body.split('\n\n').slice(1).join('\n\n').trim(); } _parseAuthorOrCommitter(type) { const r = new RegExp(`^${type} ([^<>]+) <(\\S+)> (\\d+) ([+-]?\\d{2})(\\d{2})$`, 'm'); const [, name, email, dateStr, tzHourStr, tzMinuteStr] = this.body.match(r); const time = +dateStr * 1000; const date = new Date(time); const timezoneOffset = (+tzHourStr * 60 + +tzMinuteStr) * 60 * 1000; return { name, email, date, timezoneOffset }; } get author() { if (this._author) { return this._author; } return this._author = this._parseAuthorOrCommitter('author'); } get committer() { if (this._committer) { return this._committer; } return this._committer = this._parseAuthorOrCommitter('committer'); } get hasParents() { return !!this.parentHashes.length; } get isMergeCommit() { return this.parentHashes.length >= 2; } get baseParentHash() { return this.parentHashes[0]; } get mergedParentHashes() { return this.parentHashes.slice(1); } /** * Get parent commit by a parent hash. * * ```ts * // default is the baseParentHash. * let parentCommit = await commit.walk(); * * // or set a parent hash manually. * parentCommit = await commit.walk(commit.parentHash[1]); * * // same mean * parentCommit = await commit.walk(1); * ``` */ walk(parentHash = 0) { return __awaiter(this, void 0, void 0, function* () { if (typeof parentHash === 'number') parentHash = this.parentHashes[parentHash]; return yield Commit.readCommit(this.gitDir, parentHash, this._packs); }); } /** * Get parent commit by a parent hash sync. * * ```ts * let parentCommit = commit.walkSync(); * parentCommit = commit.walkSync(commit.parentHash[1]); * ``` */ walkSync(parentHash = 0) { if (typeof parentHash === 'number') parentHash = this.parentHashes[parentHash]; return Commit.readCommitSync(this.gitDir, parentHash, this._packs); } static readCommit(gitDir, hash, packs) { return __awaiter(this, void 0, void 0, function* () { if (packs && packs.hasPackFiles) { try { const body = yield packs.unpackGitObject(hash); const s = body.toString('utf8'); if (s.startsWith('object')) { return yield Commit.readCommit(gitDir, getCommitHashFromAnnotatedTag(s), packs); } return new Commit(gitDir, hash, createCommitData(body), packs); } catch (e) { if (e instanceof InvalidAnnotatedTagError) throw e; } } const deflatedData = yield readFileAsync(getObjectPath(gitDir, hash)); const data = (yield inflateAsync(deflatedData)).toString('utf8'); if (data.startsWith('tag')) { return yield Commit.readCommit(gitDir, getCommitHashFromAnnotatedTag(data), packs); } return new Commit(gitDir, hash, data, packs); }); } static readCommitSync(gitDir, hash, packs) { if (packs && packs.hasPackFiles) { try { const body = packs.unpackGitObjectSync(hash); const s = body.toString('utf8'); if (s.startsWith('object')) { return Commit.readCommitSync(gitDir, getCommitHashFromAnnotatedTag(s), packs); } return new Commit(gitDir, hash, createCommitData(body), packs); } catch (e) { if (e instanceof InvalidAnnotatedTagError) throw e; } } const deflatedData = fs.readFileSync(getObjectPath(gitDir, hash)); const data = zlib.inflateSync(deflatedData).toString('utf8'); if (data.startsWith('tag')) { return Commit.readCommitSync(gitDir, getCommitHashFromAnnotatedTag(data), packs); } return new Commit(gitDir, hash, data, packs); } } exports.Commit = Commit; function createCommitData(body) { return `commit ${body.length}\u0000${body.toString('utf8')}`; } function getObjectPath(gitDir, hash) { return path.join(gitDir, 'objects', hash.replace(/^(.{2})(.{38})$/, `$1${path.sep}$2`)); } function getGitObjectBody(s) { let i = 0; while (s[i++] !== '\u0000') ; return s.slice(i); } class InvalidAnnotatedTagError extends Error { constructor(message) { super(message); this.message = `Invalid git object type: ${message}`; } } function getCommitHashFromAnnotatedTag(tagBody) { if (tagBody.startsWith('tag')) tagBody = getGitObjectBody(tagBody); const lines = tagBody.split('\n'); const tag = {}; lines.some(line => { if (!line.length) return true; const [k, v] = line.split(/\s/); switch (k) { case 'object': case 'type': tag[k] = v; } return false; }); if (tag.type !== 'commit') throw new InvalidAnnotatedTagError(tag.type); return tag.object; } //# sourceMappingURL=commit.js.map