UNPKG

sourcecontrol

Version:

A modern TypeScript CLI application for source control

144 lines 5.12 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GitIndex = void 0; const index_entry_1 = require("./index-entry"); const utils_1 = require("../../utils"); const crypto_1 = require("crypto"); const exceptions_1 = require("../exceptions"); class GitIndex { constructor(version = GitIndex.VERSION, entries = []) { this.version = version; this.entries = entries; this.sortEntries(); } static async read(indexPath) { if (!(await utils_1.FileUtils.exists(indexPath))) { return new GitIndex(); } const data = await utils_1.FileUtils.readFile(indexPath); return GitIndex.deserialize(new Uint8Array(data)); } async write(indexPath) { const data = this.serialize(); await utils_1.FileUtils.createFile(indexPath, data); } entryNames() { return this.entries.map((e) => e.filePath); } removeEntry(path) { this.entries = this.entries.filter((e) => e.filePath !== path); } hasEntry(path) { return this.entries.some((e) => e.filePath === path); } add(entry) { this.entries.push(entry); } getEntry(path) { return this.entries.find((e) => e.filePath === path); } clear() { this.entries = []; } serialize() { const entriesSize = this.entries.reduce((sum, entry) => { const entryData = entry.serialize(); return sum + entryData.length; }, 0); const totalSize = GitIndex.HEADER_SIZE + entriesSize + GitIndex.CHECKSUM_SIZE; const buffer = new Uint8Array(totalSize); const view = new DataView(buffer.buffer); let offset = 0; buffer[offset++] = 0x44; buffer[offset++] = 0x49; buffer[offset++] = 0x52; buffer[offset++] = 0x43; view.setUint32(offset, this.version, false); offset += 4; view.setUint32(offset, this.entries.length, false); offset += 4; this.entries.forEach((entry) => { const entryData = entry.serialize(); buffer.set(entryData, offset); offset += entryData.length; }); const contentHash = buffer.slice(0, offset); const checksum = (0, crypto_1.createHash)('sha1').update(contentHash).digest(); buffer.set(checksum, offset); return buffer; } static deserialize(data) { const view = new DataView(data.buffer, data.byteOffset); let offset = 0; const validateSignature = () => { const signature = new TextDecoder().decode(data.slice(0, 4)); if (signature !== GitIndex.SIGNATURE) { throw new exceptions_1.ObjectException(`Invalid index signature: ${signature}`); } offset += 4; }; const validateVersion = () => { const version = view.getUint32(offset, false); offset += 4; if (version !== GitIndex.VERSION) { throw new exceptions_1.ObjectException(`Unsupported index version: ${version}`); } }; const readEntries = () => { const entryCount = view.getUint32(offset, false); offset += 4; const entries = []; for (let i = 0; i < entryCount; i++) { const { entry, nextOffset } = index_entry_1.IndexEntry.deserialize(data, offset); entries.push(entry); offset = nextOffset; } return entries; }; const validateChecksum = () => { const contentSize = data.length - GitIndex.CHECKSUM_SIZE; const content = data.slice(0, contentSize); const expectedChecksum = data.slice(contentSize); const actualChecksum = (0, crypto_1.createHash)('sha1').update(content).digest(); if (!GitIndex.compareChecksums(expectedChecksum, actualChecksum)) { throw new exceptions_1.ObjectException('Index checksum mismatch'); } }; validateSignature(); validateVersion(); const entries = readEntries(); validateChecksum(); return new GitIndex(GitIndex.VERSION, entries); } isEntryModified(entry, stats) { if (entry.fileSize !== stats.size) { return true; } const mtimeSeconds = Math.floor(stats.mtimeMs / 1000); if (entry.modificationTime.seconds !== mtimeSeconds) { return true; } if (entry.assumeValid) { return false; } return false; } static compareChecksums(a, b) { if (a.length !== b.length) return false; for (let i = 0; i < a.length; i++) { if (a[i] !== b[i]) return false; } return true; } sortEntries() { this.entries.sort((a, b) => a.compareTo(b)); } } exports.GitIndex = GitIndex; GitIndex.SIGNATURE = 'DIRC'; GitIndex.VERSION = 2; GitIndex.HEADER_SIZE = 12; GitIndex.CHECKSUM_SIZE = 20; //# sourceMappingURL=git-index.js.map