I am stuck in just last stage of git clone command in java script
if someone could tell me what to fix it would be helpful
here is the code
const fs = require(“fs”);
const path = require(“path”);
const zlib = require(“zlib”);
const crypto = require(“crypto”);
const https = require(“https”);
const command = process.argv[2];
switch (command) {
case “init”:
createGitDirectory();
break;
case "cat-file":
if (process.argv.length < 5) {
console.error('Missing arguments. Use: ./your_git.sh cat-file <option> <object>');
break;
}
const option = process.argv[3];
const object = process.argv[4];
const content = getCatFile(option, object);
if (content !== null) {
console.log(content);
}
break;
case "hash-object":
hashObject();
break;
case "ls-tree":
if (process.argv.length < 5) {
console.log('Missing argument: <hash>');
break;
}
lsTree();
break;
case "write-tree":
process.stdout.write(writeTree());
break;
case "commit-tree":
if (process.argv.length < 8) {
console.log('Missing argument: <tree> <parent> <message>')
break;
}
const tree = process.argv[3]
const parent = process.argv[5]
const message = process.argv[7]
commitTree(tree, message, parent)
break;
case "clone":
if (process.argv.length < 5) {
console.log('Usage: node script.js clone <repository_url> <target_directory>');
break;
}
const repoUrl = process.argv[3];
const targetDir = process.argv[4];
cloneRepository(repoUrl, targetDir);
break;
default:
throw new Error(`Unknown command ${command}`);
}
function createGitDirectory() {
const gitDir = path.join(__dirname, “.git”);
fs.mkdirSync(gitDir, { recursive: true });
fs.mkdirSync(path.join(gitDir, “objects”), { recursive: true });
fs.mkdirSync(path.join(gitDir, “refs”), { recursive: true });
fs.writeFileSync(path.join(gitDir, "HEAD"), "ref: refs/heads/main\n");
console.log("Initialized git directory");
}
function getCatFile(option, object) {
switch (option) {
case “-p”:
const folder = object.substring(0, 2);
const file = object.substring(2);
const contents = fs.readFileSync(path.join(__dirname, ".git", "objects", folder, file));
const decompress = zlib.inflateSync(contents);
// Remove the prefix "blob 38\x00" from the content
const content = decompress.toString('utf-8').split('\0')[1];
process.stdout.write(content);
default:
console.error("Invalid usage. Use: ./your_git.sh cat-file -p <object>");
return null;
}
}
function hashObject() {
if (process.argv[3] !== “-w”) return;
const file = process.argv[4];
const content = fs.readFileSync(file);
const header = `blob ${content.length}\x00`;//the reason for 00 is hexadec
const data = header + content;
const hash = crypto.createHash('sha1').update(data).digest('hex');
const objectsDirPath = path.join(__dirname, ".git", "objects");
const hashDirPath = path.join(objectsDirPath, hash.slice(0, 2));
const filePath = path.join(hashDirPath, hash.slice(2));
fs.mkdirSync(hashDirPath, { recursive: true });
fs.writeFileSync(filePath, zlib.deflateSync(data));
process.stdout.write(hash);
}
// function lsTree() {
// if (process.argv[3] !== “–name-only”) {
// throw new Error(“Only --name-only flag is supported for now”);
// }
// const hash = process.argv[4];
// // Reading dir from hash
// const [folder, file] = [hash.slice(0, 2), hash.slice(2)];
// const objPath = path.join(__dirname, “.git”, “objects”, folder, file);
// const compressedContents = fs.readFileSync(objPath);
// const deflate = zlib.inflateSync(compressedContents).toString();
// const contents = deflate.split(‘\0’).slice(1).map((line) => line.split(" “).slice(1).join(” “));
// process.stdout.write(contents.join(”\n"));
// }
function lsTree() {
if (process.argv[3] !== “–name-only”) {
throw new Error(“Only --name-only flag is supported for now”);
}
const hash = process.argv[4];
// Reading dir from hash
const [folder, file] = [hash.slice(0, 2), hash.slice(2)];
const objPath = path.join(__dirname, ".git", "objects", folder, file);
const compressedContents = fs.readFileSync(objPath);
const deflate = zlib.inflateSync(compressedContents).toString();
const contents = deflate.split('\0').slice(1).map((line) => line.split(" ").slice(1).join(" "));
const fileNames = contents.filter(Boolean); // Filter out empty strings
const output = fileNames.join("\n") + "\n"; // Ensure output ends with newline
process.stdout.write(output);
}
function writeTree(dir = __dirname) {
const filesAndDirs = fs.readdirSync(dir).filter((f) => f !== “.git” && f !== “main.js”);
const entries = ;
for (const file of filesAndDirs) {
const fullPath = path.join(dir, file);
if (fs.statSync(fullPath).isFile()) {
entries.push({
mode: 100644,
name: file,
hash: writeBlob(fullPath),
});
} else {
entries.push({
mode: 40000,
name: file,
hash: writeTree(path.join(dir, file)),
});
}
}
const contents = entries.reduce((acc, { mode, name, hash }) => {
return Buffer.concat([
acc,
Buffer.from(`${mode} ${name}\0`),
Buffer.from(hash, "hex"),
]);
}, Buffer.alloc(0));
const treeContents = Buffer.concat([
Buffer.from(`tree ${contents.length}\x00`),
contents,
]);
// Calculate the hash of the tree contents
const treeHash = hashContent(treeContents);
// Write the tree object to the git directory
fs.mkdirSync(path.join(__dirname, ".git", "objects", treeHash.slice(0, 2)), { recursive: true });
fs.writeFileSync(
path.join(__dirname, ".git", "objects", treeHash.slice(0, 2), treeHash.slice(2)),
zlib.deflateSync(treeContents)
);
return treeHash;
}
function hashContent(data) {
return crypto.createHash(“sha1”).update(data).digest(“hex”);
}
function writeBlob(file) {
const fileContent = fs.readFileSync(file, “utf-8”);
const blob = blob ${fileContent.length}\x00${fileContent}
;
const hash = crypto.createHash(“sha1”).update(blob).digest(“hex”);
const directory = hash.slice(0, 2);
const filename = hash.slice(2);
fs.mkdirSync(path.join(__dirname, “.git”, “objects”, directory), {
recursive: true,
});
fs.writeFileSync(
path.join(__dirname, “.git”, “objects”, directory, filename),
zlib.deflateSync(blob)
);
return hash;
}
function commitTree(tree, message, parent = null) {
const author = ‘Dev’;
const authorEmail = ‘dev.sra2180@gmail.com’;
let contents = Buffer.from(‘tree ’ + tree + ‘\n’);
if (parent) {
contents = Buffer.concat([contents, Buffer.from(‘parent ’ + parent + ‘\n’)]);
}
const now = new Date();
const timestamp = now.valueOf();
const offset = now.getTimezoneOffset();
contents = Buffer.concat([
contents,
Buffer.from(author ${author} <${authorEmail}> ${timestamp} ${offset}\n
),
]);
contents = Buffer.concat([
contents,
Buffer.from(committer ${author} <${authorEmail}> ${timestamp} ${offset}\n
),
]);
contents = Buffer.concat([contents, Buffer.from(’\n’)]);
contents = Buffer.concat([contents, Buffer.from(message + ‘\n’)]);
const header = 'commit ’ + contents.length + ‘\0’;
const data = Buffer.concat([Buffer.from(header), contents]);
const hash = hashContent(data);
const dirPath = path.join(__dirname, '.git', 'objects', hash.slice(0, 2));
fs.mkdirSync(path.join(dirPath), {
recursive: true,
});
const compressed = zlib.deflateSync(data);
fs.writeFileSync(path.join(dirPath, hash.slice(2)), compressed);
process.stdout.write(hash);
return hash;
}
function cloneRepository(repoUrl, targetDir) {
// Fetch the repository data from the provided URL
const repoName = path.basename(repoUrl, “.git”);
const options = {
hostname: “api.github.com”,
path: /repos/${repoUrl}
, // Include the full repository URL
method: “GET”,
headers: {
“User-Agent”: “Node.js”,
},
};
console.log("Fetching repository info from:", `https://${options.hostname}${options.path}`);
const req = https.get(options, (res) => {
console.log("Response status code:", res.statusCode);
if (res.statusCode === 200) {
let data = "";
res.on("data", (chunk) => {
data += chunk;
});
res.on("end", () => {
const repoInfo = JSON.parse(data);
const cloneUrl = repoInfo.clone_url;
const cloneDir = path.join(targetDir, repoName);
console.log("Cloning repository from:", cloneUrl);
console.log("Into directory:", cloneDir);
// Clone the repository by fetching its contents
https.get(cloneUrl, (res) => {
if (res.statusCode === 200) {
const tarFile = fs.createWriteStream(`${cloneDir}.tar.gz`);
res.pipe(tarFile);
tarFile.on("finish", () => {
tarFile.close(() => {
// Extract the cloned repository
extractRepository(`${cloneDir}.tar.gz`, cloneDir);
});
});
} else {
console.error(`Failed to clone repository: ${repoUrl}. Status code: ${res.statusCode}`);
}
}).on("error", (err) => {
console.error(`Error while fetching repository: ${err}`);
});
});
} else if (res.statusCode === 404) {
console.error(`Repository does not exist: ${repoUrl}`);
} else {
console.error(`Failed to fetch repository info: ${res.statusCode}`);
}
});
req.on("error", (err) => {
console.error(`Error while fetching repository info: ${err}`);
});
}
function extractRepository(tarFilePath, extractDir) {
// Check if the tar.gz file exists
if (!fs.existsSync(tarFilePath)) {
console.error(Error: Tar file does not exist: ${tarFilePath}
);
return;
}
// Check if the extraction directory exists, create it if not
if (!fs.existsSync(extractDir)) {
fs.mkdirSync(extractDir, { recursive: true });
}
// Create a read stream for the tar.gz file
const readStream = fs.createReadStream(tarFilePath);
readStream.on("error", (err) => {
console.error("Error reading tar file:", err);
});
// Create a gunzip transform stream
const gunzip = zlib.createGunzip();
gunzip.on("error", (err) => {
console.error("Error decompressing tar file:", err);
});
// Create a tar extraction stream
const extractStream = tar.extract({ cwd: extractDir });
extractStream.on("error", (err) => {
console.error("Error extracting tar contents:", err);
});
// Pipe the read stream through the gunzip and tar extraction streams
readStream.pipe(gunzip)
.on("error", (err) => {
console.error("Error piping read stream to gunzip:", err);
})
.pipe(extractStream)
.on("error", (err) => {
console.error("Error piping gunzip stream to tar extraction:", err);
})
.on("end", () => {
// Once extraction is complete, remove the tar.gz file
fs.unlinkSync(tarFilePath);
});
}