Stuck at Stage 5

I’m stuck on Stage 5.

I’ve tried to use process.cwd() to get correct path. I am calling writeTreeObject(process.cwd()). Everything seems fine but it is saying “Git object file doesn’t match official Git implementation. Diff after zlib decompression”:

Here are my logs:

remote: [your_program] b3d30995c2638c42354040fb1384cad77c2e638d
remote: [tester::#FE4] Found git object file written at .git/objects/b3/d30995c2638c42354040fb1384cad77c2e638d.
remote: [tester::#FE4] Git object file doesn't match official Git implementation. Diff after zlib decompression:
remote: [tester::#FE4] 
remote: [tester::#FE4] Expected (bytes 0-100), hexadecimal:                        | ASCII:
remote: [tester::#FE4] 74 72 65 65 20 31 30 31 00 31 30 30 36 34 34 20 68 6f 72 73 | tree 101.100644 hors
remote: [tester::#FE4] 65 79 00 99 72 15 fe 2f 05 08 88 d0 96 ff 68 6a 47 d5 be a6 | ey..r../......hjG...
remote: [tester::#FE4] 18 3e 69 34 30 30 30 30 20 68 75 6d 70 74 79 00 f8 72 16 d6 | .>i40000 humpty..r..
remote: [tester::#FE4] 49 7e b0 29 b0 a8 69 09 e8 6c 0a 25 22 6b 3e f0 34 30 30 30 | I~.)..i..l.%"k>.4000
remote: [tester::#FE4] 30 20 76 61 6e 69 6c 6c 61 00 6e 41 88 88 4d 92 98 e9 11 54 | 0 vanilla.nA..M....T
remote: [tester::#FE4] 
remote: [tester::#FE4] Actual (bytes 0-100), hexadecimal:                          | ASCII:
remote: [tester::#FE4] 74 72 65 65 20 31 30 33 00 31 30 30 36 34 34 20 68 6f 72 73 | tree 103.100644 hors
remote: [tester::#FE4] 65 79 00 99 72 15 fe 2f 05 08 88 d0 96 ff 68 6a 47 d5 be a6 | ey..r../......hjG...
remote: [tester::#FE4] 18 3e 69 30 34 30 37 35 35 20 68 75 6d 70 74 79 00 f8 72 16 | .>i040755 humpty..r.
remote: [tester::#FE4] d6 49 7e b0 29 b0 a8 69 09 e8 6c 0a 25 22 6b 3e f0 30 34 30 | .I~.)..i..l.%"k>.040
remote: [tester::#FE4] 37 35 35 20 76 61 6e 69 6c 6c 61 00 6e 41 88 88 4d 92 98 e9 | 755 vanilla.nA..M...
remote: [tester::#FE4] 
remote: [tester::#FE4] Git object file doesn't match official Git implementation
remote: [tester::#FE4] Test failed (try setting 'debug: true' in your codecrafters.yml to see more details)

And here’s a snippet of my code:

async writeTreeObject(root: string): Promise<string> {
        const files = fs.readdirSync(path.resolve(root))
                        .sort((a, b) => a.localeCompare(b));
        // console.log(files);
        let contentToWrite = Buffer.alloc(0);
        for(let file of files) {
            if(file === GitPath.Git) {
                continue;
            }
            let shaHash: string = "";
            const resolvedPath = path.join(root, file);
            const stat = fs.lstatSync(resolvedPath);
            if(stat.isDirectory()) {
                // console.log(`${file} is a directory.`);
                shaHash = await this.writeTreeObject(resolvedPath);
            } else if(stat.isFile()) {
                // console.log(`${file} is a file.`);
                shaHash = await this.writeBlobObject(resolvedPath);
            }

            contentToWrite = Buffer.concat([contentToWrite, 
                Buffer.from(`${this.#getFileMode(resolvedPath)} ${file}\0`), 
                Buffer.from(shaHash, 'hex')]);
        }
        let dataToWrite = Buffer.concat([Buffer.from(`tree ${contentToWrite.length}\0`), contentToWrite]);
        // console.log(dataToWrite);
        return await this.#writeData(dataToWrite);
    }

    /**
     * Writes the blob object to the file system.
     * @param data The data to write.
     */
    async writeBlobObject(file: string): Promise<string> {
        let data = fs.readFileSync(file);
        let dataToWrite = Buffer.concat([Buffer.from(`blob ${data.length}\0`), data]);
        return await this.#writeData(dataToWrite);
    }

    #getFileMode(path: string): string {
        const fileMode = fs.statSync(path).mode
        // console.log(fileMode);
        const fileType = +(fileMode & 0o170000).toString(8)
        const permissions = +(fileMode & 0o777).toString(8)
        const mode = fileType + permissions
        // console.log(mode.toString().padStart(6, '0'));
        return mode.toString().padStart(6, '0')
      }

    #writeData(data: Buffer): Promise<string> {
        let shaDigest = this.#calculateShaDigest(data);
        let pathToFileURL = this.#createPathFrom(shaDigest);
        let stream = Readable.from(data);

        return new Promise((resolve, reject) => {
            stream.pipe(
                zlib.createDeflate()
            ).pipe(
                fs.createWriteStream(pathToFileURL)
            ).on('finish', () => {
                resolve(shaDigest);
            }).on('error', (error: Error) => {
                reject(error);
            });
        });
    }


    #calculateShaDigest(data: Buffer): string {
        let shaDigest = require('crypto').createHash('sha1');
        shaDigest.update(data);
        return shaDigest.digest('hex');
    }

    

    #createPathFrom(shaDigest: string): string {
        let objectPath = path.join(GitPath.Objects, shaDigest.slice(0, 2));
        if (!fs.existsSync(objectPath)) {
            fs.mkdirSync(objectPath, { recursive: true });
        }

        let pathToFileURL = path.join(objectPath, shaDigest.slice(2));
        if(!fs.existsSync(pathToFileURL)) {
            fs.openSync(pathToFileURL, 'w+');
        }
        // console.log(pathToFileURL);
        return pathToFileURL;
    }

@Shubho2 looks like the logs contain the diff explaining what is different vs expected - does that help?

It does show the difference. But I am not getting what I am missing. Am I calculating content length wrongly?

Not sure about the content length (that’s most likely a consequence of incorrect content), but from the diff what stands out to me is the file mode difference:

40000 (expected)

vs.

040755 (incorrect)

1 Like

Thanks, the extra padding of zero was the issue. So, during ls-tree 0 is added, not during the writing of the tree. It is resolved now.

1 Like

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.