From 41dd622220183b3c7b27800b018d10124dd775d0 Mon Sep 17 00:00:00 2001 From: ulxsth Date: Thu, 27 Mar 2025 11:22:05 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20saveBlob=20=E3=82=92=E5=AE=9F?= =?UTF-8?q?=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example | 1 + src/{loader.ts => blob.ts} | 27 ++++++++++++++++++++++++ src/mygit.ts | 42 +++++++++++++++----------------------- 3 files changed, 45 insertions(+), 25 deletions(-) create mode 160000 example rename src/{loader.ts => blob.ts} (63%) diff --git a/example b/example new file mode 160000 index 0000000..5fd5ee8 --- /dev/null +++ b/example @@ -0,0 +1 @@ +Subproject commit 5fd5ee806918deb683e2c2738e85600f7f0c023a diff --git a/src/loader.ts b/src/blob.ts similarity index 63% rename from src/loader.ts rename to src/blob.ts index 4aaab39..a2f52e6 100644 --- a/src/loader.ts +++ b/src/blob.ts @@ -1,3 +1,4 @@ +import { createHash } from "crypto"; import fs from "fs"; import path from "path"; import zlib from "zlib"; @@ -40,6 +41,32 @@ const loadBlob = (hash: string): GitBlobObject => { return {type, size: Number(size), content}; }; +const saveBlob = (blob: GitBlobObject): void => { + const contentBuffer = Buffer.from(blob.content, "utf-8"); + const header = `${blob.type} ${contentBuffer.length.toString()}\0`; + + const headerUInt8Array = new Uint8Array(Buffer.from(header, "utf-8")); + const blobBuffer = Buffer.concat([headerUInt8Array, new Uint8Array(contentBuffer)]); + + // Compress the blob data + const compressed = zlib.deflateSync(new Uint8Array(blobBuffer)); + + // Calculate the hash of the blob + const hash = createHash("sha1").update(new Uint8Array(blobBuffer)).digest("hex"); + + + // Write the compressed blob to the file + const cwd = process.cwd(); + const dirName = hash.slice(0, 2); + const fileName = hash.slice(2); + const dirPath = path.join(cwd, ".git", "objects", dirName); + if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath, { recursive: true }); + } + const filePath = path.join(dirPath, fileName); + fs.writeFileSync(filePath, new Uint8Array(compressed)); +}; + const toArrayBuffer = (buffer: Buffer): ArrayBuffer => { const ab = new ArrayBuffer(buffer.length); const view = new Uint8Array(ab); diff --git a/src/mygit.ts b/src/mygit.ts index e85b721..1bb17ef 100644 --- a/src/mygit.ts +++ b/src/mygit.ts @@ -9,31 +9,23 @@ const __dirname = path.dirname(__filename); export const mygit = async (argv: Array): Promise => { const commandName = argv[2] ?? ""; - - const commandsDirPath = path.join(__dirname, "commands"); - const commandsDir = fs.readdirSync(commandsDirPath) - .filter((file) => file.endsWith(".js")); - - for (const file of commandsDir) { - if(file.replace(".js", "") !== commandName) { - continue; - } - - // * ESM Module まわりが any を使わざるを得ないので、ESLint 側の制限を緩めてます - const commandsPath = path.join(commandsDirPath, file); - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const commandModule = await import(commandsPath); - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access - const command: Command = commandModule.default; - - if (typeof command.exec === "function") { - command.exec(argv); - return; - } + + switch (commandName) { + case "log": + break; + + case "add": + break; + + case "commit": + break; + + default: + console.log("サポートされていない引数です ><"); } +} + - console.log("サポートされていない引数です ><"); - - // Avoid eslint error by adding some async operation. - await new Promise((resolve) => setTimeout(resolve, 1000)); +// Avoid eslint error by adding some async operation. +await new Promise((resolve) => setTimeout(resolve, 1000)); }; From af73e12af2e9070a41388fd3ebb072d51847299e Mon Sep 17 00:00:00 2001 From: ulxsth Date: Thu, 27 Mar 2025 11:38:56 +0900 Subject: [PATCH 2/4] =?UTF-8?q?refactor:=20blob2UIntArray=20=E3=82=92?= =?UTF-8?q?=E6=8A=BD=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/blob.ts | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/blob.ts b/src/blob.ts index a2f52e6..3a9e993 100644 --- a/src/blob.ts +++ b/src/blob.ts @@ -42,18 +42,13 @@ const loadBlob = (hash: string): GitBlobObject => { }; const saveBlob = (blob: GitBlobObject): void => { - const contentBuffer = Buffer.from(blob.content, "utf-8"); - const header = `${blob.type} ${contentBuffer.length.toString()}\0`; - - const headerUInt8Array = new Uint8Array(Buffer.from(header, "utf-8")); - const blobBuffer = Buffer.concat([headerUInt8Array, new Uint8Array(contentBuffer)]); + const blobBuffer = blob2UInt8Array(blob); // Compress the blob data - const compressed = zlib.deflateSync(new Uint8Array(blobBuffer)); + const compressed = zlib.deflateSync(blobBuffer); // Calculate the hash of the blob - const hash = createHash("sha1").update(new Uint8Array(blobBuffer)).digest("hex"); - + const hash = createHash("sha1").update(blobBuffer).digest("hex"); // Write the compressed blob to the file const cwd = process.cwd(); @@ -64,7 +59,20 @@ const saveBlob = (blob: GitBlobObject): void => { fs.mkdirSync(dirPath, { recursive: true }); } const filePath = path.join(dirPath, fileName); - fs.writeFileSync(filePath, new Uint8Array(compressed)); + fs.writeFileSync(filePath, compressed); +}; + +const blob2UInt8Array = (blob: GitBlobObject): Uint8Array => { + const contentBuffer = Buffer.from(blob.content, "utf-8"); + const header = `${blob.type} ${contentBuffer.length.toString()}\0`; + const headerBuffer = Buffer.from(header, "utf-8"); + return Buffer.concat([headerBuffer, contentBuffer].map((b) => new Uint8Array(b))); +}; + + +const blob2Hash = (blob: GitBlobObject): string => { + const blobBuffer = blob2UInt8Array(blob); + return createHash("sha1").update(blobBuffer).digest("hex"); }; const toArrayBuffer = (buffer: Buffer): ArrayBuffer => { @@ -77,4 +85,8 @@ const toArrayBuffer = (buffer: Buffer): ArrayBuffer => { return ab; } -console.log(loadBlob("78981922613b2afb6025042ff6bd878ac1994e85")); \ No newline at end of file +// test +const data: GitBlobObject = {type: "blob", size: 5, content: "Hello\n"}; +saveBlob(data); +console.log(blob2Hash(data)); +console.log(loadBlob(blob2Hash(data))); \ No newline at end of file From 1cd75a43c11a078fa50b0c063ef7d154ac029b99 Mon Sep 17 00:00:00 2001 From: ulxsth Date: Thu, 27 Mar 2025 11:43:47 +0900 Subject: [PATCH 3/4] fix: type --- src/blob.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/blob.ts b/src/blob.ts index 3a9e993..b05df63 100644 --- a/src/blob.ts +++ b/src/blob.ts @@ -3,7 +3,7 @@ import fs from "fs"; import path from "path"; import zlib from "zlib"; -type GitBlobObject = { +interface GitBlobObject { type: "blob"; size: number; content: string; @@ -59,14 +59,15 @@ const saveBlob = (blob: GitBlobObject): void => { fs.mkdirSync(dirPath, { recursive: true }); } const filePath = path.join(dirPath, fileName); - fs.writeFileSync(filePath, compressed); + fs.writeFileSync(filePath, new Uint8Array(compressed)); }; const blob2UInt8Array = (blob: GitBlobObject): Uint8Array => { const contentBuffer = Buffer.from(blob.content, "utf-8"); const header = `${blob.type} ${contentBuffer.length.toString()}\0`; const headerBuffer = Buffer.from(header, "utf-8"); - return Buffer.concat([headerBuffer, contentBuffer].map((b) => new Uint8Array(b))); + const blobBuffer = Buffer.concat([headerBuffer, contentBuffer].map((b) => new Uint8Array(b))); + return new Uint8Array(blobBuffer); }; From 8de7d1eb7e2966e223accd359837a4430a52849d Mon Sep 17 00:00:00 2001 From: ulxsth Date: Thu, 27 Mar 2025 11:49:29 +0900 Subject: [PATCH 4/4] =?UTF-8?q?refactor:=20docstring=20=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/blob.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/blob.ts b/src/blob.ts index b05df63..2154a98 100644 --- a/src/blob.ts +++ b/src/blob.ts @@ -9,6 +9,11 @@ interface GitBlobObject { content: string; } +/** + * .git/object/ 以下に保存された Git Blob オブジェクトを読み込む。 + * @param hash Git Blob オブジェクトのハッシュ値 + * @returns GitBlobObject + */ const loadBlob = (hash: string): GitBlobObject => { const cwd = process.cwd(); const dirName = hash.slice(0, 2); @@ -34,13 +39,17 @@ const loadBlob = (hash: string): GitBlobObject => { throw new Error("Invalid Git object format: Invalid header."); } - if(type !== "blob") { + if (type !== "blob") { throw new Error("Invalid Git object format: Invalid type. (excepted: blob)"); } - return {type, size: Number(size), content}; + return { type, size: Number(size), content }; }; +/** + * Git Blob オブジェクトを .git/object/ 以下に保存する。 + * @param blob GitBlobObject + */ const saveBlob = (blob: GitBlobObject): void => { const blobBuffer = blob2UInt8Array(blob); @@ -62,6 +71,8 @@ const saveBlob = (blob: GitBlobObject): void => { fs.writeFileSync(filePath, new Uint8Array(compressed)); }; + + const blob2UInt8Array = (blob: GitBlobObject): Uint8Array => { const contentBuffer = Buffer.from(blob.content, "utf-8"); const header = `${blob.type} ${contentBuffer.length.toString()}\0`; @@ -70,7 +81,6 @@ const blob2UInt8Array = (blob: GitBlobObject): Uint8Array => { return new Uint8Array(blobBuffer); }; - const blob2Hash = (blob: GitBlobObject): string => { const blobBuffer = blob2UInt8Array(blob); return createHash("sha1").update(blobBuffer).digest("hex"); @@ -87,7 +97,7 @@ const toArrayBuffer = (buffer: Buffer): ArrayBuffer => { } // test -const data: GitBlobObject = {type: "blob", size: 5, content: "Hello\n"}; +const data: GitBlobObject = { type: "blob", size: 5, content: "Hello\n" }; saveBlob(data); console.log(blob2Hash(data)); console.log(loadBlob(blob2Hash(data))); \ No newline at end of file