Skip to content

gix fails to decode a tree object that git has no trouble with #1096

Open
@bradlarsen

Description

@bradlarsen

Current behavior 😯

There are certain Git repositories that contain tree objects that gix fails to decode, but which git itself has no trouble with.

For example, from a test program included in this issue:

Finding tree 7dde39c37eea1cc6f81a04ba50e2ef55333c6966 from repo at /disk1/clones/jsbin/jsbin
Err(Decode(Error { inner: VerboseError { errors: [("040000 css\0\xA7E/\xD7Ǡ\x91S\x96p\xF2\xE8S˦S\x98@-\xDF100644 favicon.ico\0p\x06\x9B\xADB\x8A\x93\xD4V\xCD\x15\x93\x9D\xB2\xF1\xD66v4\x90100644 help.html\0\xBDp\xC6M$\xC7\u{1e}D\xC8@OZ>\\\xA5TU\xAB\xAE\xE5040000 help\0\xE7eo7rg g\n\x18_:\x12\xE3<\xD2!\xFF\xF9\x82040000 images\0d\x97\xB2\x82C\xAF%n\t)\xDB\x05\xD0
q\xE2\xC3\x0c\xBD\xE1C100644 index.php\0\xFF\r\x08\xC4\x0b\xCF \xFB\xEE:]\x7fQӉb\xEC1\xD6\u{1c}40000 js\0^\"\xE0P\x94\xA0bi4\xCB/s\x19\xE9-\xA2\xD3C\xE7&040000 lib\0\xAB;\xBF\xBC\x88)Y\xE8p\n=\r\xF3\xAF\xCFH\x13\xB6\x0e\x8C100644 phpMake\0<\xB0\xFF\x96Uvq\xB4\xBB\xC6,\xC1!\x17\xC78\x99<j\xE5100644 sprocketize.php\0\x82L\xD6\u{1f}\xD8\x13\xC8\x0bq\x16\x0f\xF9\x86
`5no\x9B\x10b", Nom(Eof))] } }))

Additionally, it appears that the verbose-object-parsing-errors feature of gix-object is broken when using gix newer than 0.51.0 and gix-object newer than 0.33. Rather than getting a detailed error message when an object fails to parse, the error message is None, or rendered as an empty string. This seems to coincide with switching away from nom for object parsing.

Expected behavior 🤔

gix should correctly parse the tree objects.

Git behavior

git seems to have no trouble!

Steps to reproduce 🕹

For example, see https://github.com/jsbin/jsbin, which has a few problematic trees:

  • 5e22e05094a0626934cb2f7319e92da2d343e726
  • 7dde39c37eea1cc6f81a04ba50e2ef55333c6966
  • ad20e1886a324465093f656a0910b9fe00429977
  • e0d917771d3c3ffb7782158d9f33b2b7e9c6c524

Locally, I cloned the repo to /disk1/clones/jsbin/jsbin using git clone --mirror.

We can inspect the second problematic tree (7dde39c37eea1cc6f81a04ba50e2ef55333c6966) with Git. It is the root tree of this commit: jsbin/jsbin@884c72e

$ (cd /disk1/clones/jsbin/jsbin && git cat-file -p 7dde39c37eea1cc6f81a04ba50e2ef55333c6966)
100644 blob 996ff49cd001cdbae32514ac195edb2eebacdcd0    .gitignore
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391    .gitmodules
100644 blob e5feb64e6839795c0d47ba0476b34cbb44d406dd    .htaccess
100644 blob e76d29c74827f2766941518789b74f19afb6b6d5    MIT-LICENSE.TXT
100644 blob 47c2818071f4e8c03994dea6090148ef8aca228c    README.markdown
100644 blob f5a2e73a21171b24343e4dfc44da3a4a328fbf0e    about.html
100644 blob 4ff6443cf419b2a255015ec2237f135330cf2fdd    app.php
100644 blob 21ea174a8fc08bdceb0dca1b4bb54ee159371995    config.php
040000 tree a7452fd7c7a091539670f2e853cba65398402ddf    css
100644 blob 70069bad428a93d456cd15939db2f1d636763490    favicon.ico
100644 blob bd70c64d24c71e44c8404f5a3e5ca55455abaee5    help.html
040000 tree e7656f37726720670a185f3a12e33cd221fff982    help
040000 tree 6497b28243af256e0929db05d071e2c30cbde143    images
100644 blob ff0d08c40bcf20fbee3a5d7f51d38962ec31d61c    index.php
040000 tree 5e22e05094a0626934cb2f7319e92da2d343e726    js
040000 tree ab3bbfbc882959e8700a3d0df3afcf4813b60e8c    lib
100644 blob 3cb0ff96557671b4bbc62cc12117c738993c6ae5    phpMake
100644 blob 824cd61fd813c80b71160ff98660356e6f9b1062    sprocketize.php

Git says its payload is 659 bytes long:

$ (cd /disk1/clones/jsbin/jsbin && git cat-file -s 7dde39c37eea1cc6f81a04ba50e2ef55333c6966)
659

I wrote a small gix test program that demonstrates the problem.

Cargo.toml:

[package]
name = "gix-tree-bug"
version = "0.1.0"
edition = "2021"

[dependencies]

# Note that the `verbose-object-parsing-errors` feature is broken in gix 0.52 + gix-object 0.35 and later: error messages come out as `None`, rendered as empty strings.
# The feature is not broken in gix 0.51 + gix-object 0.33.
gix = { version = "0.51" }
gix-object = { version = "0.33", features = ["verbose-object-parsing-errors"] }

src/main.rs:

// This example program shows a failure to parse a tree object using gix.
//
// The repo at `https://github.com/jsbin/jsbin` has several trees that fail to parse:
//
//   - 5e22e05094a0626934cb2f7319e92da2d343e726
//   - 7dde39c37eea1cc6f81a04ba50e2ef55333c6966
//   - ad20e1886a324465093f656a0910b9fe00429977
//   - e0d917771d3c3ffb7782158d9f33b2b7e9c6c524
//
// Note that Git itself does not have issues working with these tree objects.

use gix::prelude::*;

fn main() {
    let args: Vec<String> = std::env::args().collect();

    if args.len() != 3 {
        eprintln!("Usage: {} REPO_PATH TREE_OID", args[0]);
        return ();
    }

    let repo_path = &args[1];
    let tree_oid = &args[2];
    let tree_oid = gix::ObjectId::from_hex(tree_oid.as_bytes()).expect("Should be able to parse tree oid");

    println!("Finding tree {tree_oid} from repo at {repo_path}");

    let opts = gix::open::Options::isolated().open_path_as_is(true);
    let repo = gix::open_opts(repo_path, opts).expect("Should be able to open repository");

    // First, try getting the tree via the odb APIs
    {
        let odb = &repo.objects;
        let mut scratch: Vec<u8> = Vec::new();
        let res_odb = odb.find_tree(tree_oid, &mut scratch);
        println!("{res_odb:?}");

    }

    // Next, try getting the tree via the repo APIs
    {
        let res_repo = repo.find_object(tree_oid);
        println!("{res_repo:?}");

        let obj = res_repo.unwrap().try_into_tree().unwrap();
        println!("{obj:?}");

        println!("{}", obj.data.len());
        println!("{:?}", obj.data);

        let decoded = obj.decode();
        println!("{decoded:?}");
    }
}

Running this for the second bad tree object noted above, we get this:

$ cargo run -- /disk1/clones/jsbin/jsbin 7dde39c37eea1cc6f81a04ba50e2ef55333c6966
    Finished dev [unoptimized + debuginfo] target(s) in 0.08s
     Running `target/debug/gix-tree-bug /disk1/clones/jsbin/jsbin 7dde39c37eea1cc6f81a04ba50e2ef55333c6966`
Finding tree 7dde39c37eea1cc6f81a04ba50e2ef55333c6966 from repo at /disk1/clones/jsbin/jsbin
Err(Decode(Error { inner: VerboseError { errors: [("040000 css\0\xA7E/\xD7Ǡ\x91S\x96p\xF2\xE8S˦S\x98@-\xDF100644 favicon.ico\0p\x06\x9B\xADB\x8A\x93\xD4V\xCD\x15\x93\x9D\xB2\xF1\xD66v4\x90100644 help.html\0\xBDp\xC6M$\xC7\u{1e}D\xC8@OZ>\\\xA5TU\xAB\xAE\xE5040000 help\0\xE7eo7rg g\n\x18_:\x12\xE3<\xD2!\xFF\xF9\x82040000 images\0d\x97\xB2\x82C\xAF%n\t)\xDB\x05\xD0q\xE2\xC3\x0c\xBD\xE1C100644 index.php\0\xFF\r\x08\xC4\x0b\xCF \xFB\xEE:]\x7fQӉb\xEC1\xD6\u{1c}40000 js\0^\"\xE0P\x94\xA0bi4\xCB/s\x19\xE9-\xA2\xD3C\xE7&040000 lib\0\xAB;\xBF\xBC\x88)Y\xE8p\n=\r\xF3\xAF\xCFH\x13\xB6\x0e\x8C100644 phpMake\0<\xB0\xFF\x96Uvq\xB4\xBB\xC6,\xC1!\x17\xC78\x99<j\xE5100644 sprocketize.php\0\x82L\xD6\u{1f}\xD8\x13\xC8\x0bq\x16\x0f\xF9\x86`5no\x9B\x10b", Nom(Eof))] } }))
Ok(Tree(7dde39c37eea1cc6f81a04ba50e2ef55333c6966))
Tree(7dde39c37eea1cc6f81a04ba50e2ef55333c6966)
659
[49, 48, 48, 54, 52, 52, 32, 46, 103, 105, 116, 105, 103, 110, 111, 114, 101, 0, 153, 111, 244, 156, 208, 1, 205, 186, 227, 37, 20, 172, 25, 94, 219, 46, 235, 172, 220, 208, 49, 48, 48, 54, 52, 52, 32, 46, 103, 105, 116, 109, 111, 100, 117, 108, 101, 115, 0, 230, 157, 226, 155, 178, 209, 214, 67, 75, 139, 41, 174, 119, 90, 216, 194, 228, 140, 83, 145, 49, 48, 48, 54, 52, 52, 32, 46, 104, 116, 97, 99, 99, 101, 115, 115, 0, 229, 254, 182, 78, 104, 57, 121, 92, 13, 71, 186, 4, 118, 179, 76, 187, 68, 212, 6, 221, 49, 48, 48, 54, 52, 52, 32, 77, 73, 84, 45, 76, 73, 67, 69, 78, 83, 69, 46, 84, 88, 84, 0, 231, 109, 41, 199, 72, 39, 242, 118, 105, 65, 81, 135, 137, 183, 79, 25, 175, 182, 182, 213, 49, 48, 48, 54, 52, 52, 32, 82, 69, 65, 68, 77, 69, 46, 109, 97, 114, 107, 100, 111, 119, 110, 0, 71, 194, 129, 128, 113, 244, 232, 192, 57, 148, 222, 166, 9, 1, 72, 239, 138, 202, 34, 140, 49, 48, 48, 54, 52, 52, 32, 97, 98, 111, 117, 116, 46, 104, 116, 109, 108, 0, 245, 162, 231, 58, 33, 23, 27, 36, 52, 62, 77, 252, 68, 218, 58, 74, 50, 143, 191, 14, 49, 48, 48, 54, 52, 52, 32, 97, 112, 112, 46, 112, 104, 112, 0, 79, 246, 68, 60, 244, 25, 178, 162, 85, 1, 94, 194, 35, 127, 19, 83, 48, 207, 47, 221, 49, 48, 48, 54, 52, 52, 32, 99, 111, 110, 102, 105, 103, 46, 112, 104, 112, 0, 33, 234, 23, 74, 143, 192, 139, 220, 235, 13, 202, 27, 75, 181, 78, 225, 89, 55, 25, 149, 48, 52, 48, 48, 48, 48, 32, 99, 115, 115, 0, 167, 69, 47, 215, 199, 160, 145, 83, 150, 112, 242, 232, 83, 203, 166, 83, 152, 64, 45, 223, 49, 48, 48, 54, 52, 52, 32, 102, 97, 118, 105, 99, 111, 110, 46, 105, 99, 111, 0, 112, 6, 155, 173, 66, 138, 147, 212, 86, 205, 21, 147, 157, 178, 241, 214, 54, 118, 52, 144, 49, 48, 48, 54, 52, 52, 32, 104, 101, 108, 112, 46, 104, 116, 109, 108, 0, 189, 112, 198, 77, 36, 199, 30, 68, 200, 64, 79, 90, 62, 92, 165, 84, 85, 171, 174, 229, 48, 52, 48, 48, 48, 48, 32, 104, 101, 108, 112, 0, 231, 101, 111, 55, 114, 103, 32, 103, 10, 24, 95, 58, 18, 227, 60, 210, 33, 255, 249, 130, 48, 52, 48, 48, 48, 48, 32, 105, 109, 97, 103, 101, 115, 0, 100, 151, 178, 130, 67, 175, 37, 110, 9, 41, 219, 5, 208, 113, 226, 195, 12, 189, 225, 67, 49, 48, 48, 54, 52, 52, 32, 105, 110, 100, 101, 120, 46, 112, 104, 112, 0, 255, 13, 8, 196, 11, 207, 32, 251, 238, 58, 93, 127, 81, 211, 137, 98, 236, 49, 214, 28, 52, 48, 48, 48, 48, 32, 106, 115, 0, 94, 34, 224, 80, 148, 160, 98, 105, 52, 203, 47, 115, 25, 233, 45, 162, 211, 67, 231, 38, 48, 52, 48, 48, 48, 48, 32, 108, 105, 98, 0, 171, 59, 191, 188, 136, 41, 89, 232, 112, 10, 61, 13, 243, 175, 207, 72, 19, 182, 14, 140, 49, 48, 48, 54, 52, 52, 32, 112, 104, 112, 77, 97, 107, 101, 0, 60, 176, 255, 150, 85, 118, 113, 180, 187, 198, 44, 193, 33, 23, 199, 56, 153, 60, 106, 229, 49, 48, 48, 54, 52, 52, 32, 115, 112, 114, 111, 99, 107, 101, 116, 105, 122, 101, 46, 112, 104, 112, 0, 130, 76, 214, 31, 216, 19, 200, 11, 113, 22, 15, 249, 134, 96, 53, 110, 111, 155, 16, 98]
Err(Error { inner: VerboseError { errors: [("040000 css\0\xA7E/\xD7Ǡ\x91S\x96p\xF2\xE8S˦S\x98@-\xDF100644 favicon.ico\0p\x06\x9B\xADB\x8A\x93\xD4V\xCD\x15\x93\x9D\xB2\xF1\xD66v4\x90100644 help.html\0\xBDp\xC6M$\xC7\u{1e}D\xC8@OZ>\\\xA5TU\xAB\xAE\xE5040000 help\0\xE7eo7rg g\n\x18_:\x12\xE3<\xD2!\xFF\xF9\x82040000 images\0d\x97\xB2\x82C\xAF%n\t)\xDB\x05\xD0q\xE2\xC3\x0c\xBD\xE1C100644 index.php\0\xFF\r\x08\xC4\x0b\xCF \xFB\xEE:]\x7fQӉb\xEC1\xD6\u{1c}40000 js\0^\"\xE0P\x94\xA0bi4\xCB/s\x19\xE9-\xA2\xD3C\xE7&040000 lib\0\xAB;\xBF\xBC\x88)Y\xE8p\n=\r\xF3\xAF\xCFH\x13\xB6\x0e\x8C100644 phpMake\0<\xB0\xFF\x96Uvq\xB4\xBB\xC6,\xC1!\x17\xC78\x99<j\xE5100644 sprocketize.php\0\x82L\xD6\u{1f}\xD8\x13\xC8\x0bq\x16\x0f\xF9\x86`5no\x9B\x10b", Nom(Eof))] } })

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions