From 3613b3b6bfdc30ecdf0ebadf0e09ac944da64585 Mon Sep 17 00:00:00 2001 From: Chris Warburton Date: Mon, 23 Feb 2026 14:03:32 +0000 Subject: [PATCH 1/3] feat: support git-raw codec in get-dag --- cmd/car/get.go | 1 + cmd/car/testdata/inputs/sample-git-raw.car | Bin 0 -> 149 bytes cmd/car/testdata/script/get-dag-git-raw.txt | 8 +++++ cmd/go.mod | 1 + cmd/go.sum | 32 ++++++++++++++++++++ 5 files changed, 42 insertions(+) create mode 100644 cmd/car/testdata/inputs/sample-git-raw.car create mode 100644 cmd/car/testdata/script/get-dag-git-raw.txt diff --git a/cmd/car/get.go b/cmd/car/get.go index df71f4d6..2d20114c 100644 --- a/cmd/car/get.go +++ b/cmd/car/get.go @@ -8,6 +8,7 @@ import ( "io" "os" + _ "github.com/ipfs/go-ipld-git" dagpb "github.com/ipld/go-codec-dagpb" "github.com/ipld/go-ipld-prime" _ "github.com/ipld/go-ipld-prime/codec/cbor" diff --git a/cmd/car/testdata/inputs/sample-git-raw.car b/cmd/car/testdata/inputs/sample-git-raw.car new file mode 100644 index 0000000000000000000000000000000000000000..d7d3fbeb4570534feb3187a6b14d47a1bc945e36 GIT binary patch literal 149 zcmdN}lvV(R Nm@#Ce=H%pa0RR+EIYs~g literal 0 HcmV?d00001 diff --git a/cmd/car/testdata/script/get-dag-git-raw.txt b/cmd/car/testdata/script/get-dag-git-raw.txt new file mode 100644 index 00000000..68c946f2 --- /dev/null +++ b/cmd/car/testdata/script/get-dag-git-raw.txt @@ -0,0 +1,8 @@ +env TREE_CID='baf4bcfe6bkemw5kpijnvfjzm575jntlvrqnfhky' +env BLOB_CID='baf4bcfgoae3ckaylvdn2sbxxk2lh7hu4uokemsq' +car get-dag ${INPUTS}/sample-git-raw.car ${TREE_CID} out.car +! stderr . +car list out.car +! stderr . +stdout ${TREE_CID} +stdout ${BLOB_CID} diff --git a/cmd/go.mod b/cmd/go.mod index a7eec70c..5967cfb4 100644 --- a/cmd/go.mod +++ b/cmd/go.mod @@ -7,6 +7,7 @@ require ( github.com/ipfs/go-block-format v0.2.3 github.com/ipfs/go-cid v0.6.0 github.com/ipfs/go-ipld-format v0.6.3 + github.com/ipfs/go-ipld-git v0.1.1 github.com/ipfs/go-unixfsnode v1.10.3 github.com/ipld/go-car v0.6.3 github.com/ipld/go-car/v2 v2.16.0 diff --git a/cmd/go.sum b/cmd/go.sum index 5682c29c..1d29265c 100644 --- a/cmd/go.sum +++ b/cmd/go.sum @@ -86,6 +86,7 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/filecoin-project/go-clock v0.1.0 h1:SFbYIM75M8NnFm1yMHhN9Ahy3W5bEZV9gd6MPfXbKVU= github.com/filecoin-project/go-clock v0.1.0/go.mod h1:4uB/O4PvOjlx1VCMdZ9MyDZXRm//gkj1ELEbxfI1AZs= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= @@ -210,8 +211,11 @@ github.com/ipfs/boxo v0.36.0 h1:DarrMBM46xCs6GU6Vz+AL8VUyXykqHAqZYx8mR0Oics= github.com/ipfs/boxo v0.36.0/go.mod h1:92hnRXfP5ScKEIqlq9Ns7LR1dFXEVADKWVGH0fjk83k= github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA= github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU= +github.com/ipfs/go-block-format v0.0.3/go.mod h1:4LmD4ZUw0mhO+JSKdpWwrzATiEfM7WWgQ8H5l6P8MVk= github.com/ipfs/go-block-format v0.2.3 h1:mpCuDaNXJ4wrBJLrtEaGFGXkferrw5eqVvzaHhtFKQk= github.com/ipfs/go-block-format v0.2.3/go.mod h1:WJaQmPAKhD3LspLixqlqNFxiZ3BZ3xgqxxoSR/76pnA= +github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M= +github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= github.com/ipfs/go-cid v0.6.0 h1:DlOReBV1xhHBhhfy/gBNNTSyfOM6rLiIx9J7A4DGf30= github.com/ipfs/go-cid v0.6.0/go.mod h1:NC4kS1LZjzfhK40UGmpXv5/qD2kcMzACYJNntCUiDhQ= github.com/ipfs/go-cidutil v0.1.0 h1:RW5hO7Vcf16dplUU60Hs0AKDkQAVPVplr7lk97CFL+Q= @@ -228,10 +232,13 @@ github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1I github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= github.com/ipfs/go-ipfs-pq v0.0.4 h1:U7jjENWJd1jhcrR8X/xHTaph14PTAK9O+yaLJbjqgOw= github.com/ipfs/go-ipfs-pq v0.0.4/go.mod h1:9UdLOIIb99IFrgT0Fc53pvbvlJBhpUb4GJuAQf3+O2A= +github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ= github.com/ipfs/go-ipld-cbor v0.2.1 h1:H05yEJbK/hxg0uf2AJhyerBDbjOuHX4yi+1U/ogRa7E= github.com/ipfs/go-ipld-cbor v0.2.1/go.mod h1:x9Zbeq8CoE5R2WicYgBMcr/9mnkQ0lHddYWJP2sMV3A= github.com/ipfs/go-ipld-format v0.6.3 h1:9/lurLDTotJpZSuL++gh3sTdmcFhVkCwsgx2+rAh4j8= github.com/ipfs/go-ipld-format v0.6.3/go.mod h1:74ilVN12NXVMIV+SrBAyC05UJRk0jVvGqdmrcYZvCBk= +github.com/ipfs/go-ipld-git v0.1.1 h1:TWGnZjS0htmEmlMFEkA3ogrNCqWjIxwr16x1OsdhG+Y= +github.com/ipfs/go-ipld-git v0.1.1/go.mod h1:+VyMqF5lMcJh4rwEppV0e6g4nCCHXThLYYDpKUkJubI= github.com/ipfs/go-ipld-legacy v0.2.2 h1:DThbqCPVLpWBcGtU23KDLiY2YRZZnTkXQyfz8aOfBkQ= github.com/ipfs/go-ipld-legacy v0.2.2/go.mod h1:hhkj+b3kG9b2BcUNw8IFYAsfeNo8E3U7eYlWeAOPyDU= github.com/ipfs/go-log/v2 v2.9.1 h1:3JXwHWU31dsCpvQ+7asz6/QsFJHqFr4gLgQ0FWteujk= @@ -250,6 +257,7 @@ github.com/ipld/go-car/v2 v2.16.0 h1:LWe0vmN/QcQmUU4tr34W5Nv5mNraW+G6jfN2s+ndBco github.com/ipld/go-car/v2 v2.16.0/go.mod h1:RqFGWN9ifcXVmCrTAVnfnxiWZk1+jIx67SYhenlmL34= github.com/ipld/go-codec-dagpb v1.7.0 h1:hpuvQjCSVSLnTnHXn+QAMR0mLmb1gA6wl10LExo2Ts0= github.com/ipld/go-codec-dagpb v1.7.0/go.mod h1:rD3Zg+zub9ZnxcLwfol/OTQRVjaLzXypgy4UqHQvilM= +github.com/ipld/go-ipld-prime v0.11.0/go.mod h1:+WIAkokurHmZ/KwzDOMUuoeJgaRQktHtEaLglS3ZeV8= github.com/ipld/go-ipld-prime v0.22.0 h1:YJhDhjEOvOYaqshd3b4atIWUoRg/rKrgmwCyUHwlbuY= github.com/ipld/go-ipld-prime v0.22.0/go.mod h1:ol7vKxOOVgEh0iAPuiDalM+0gScXVMA5ZZa4DVrTnEA= github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20250821084354-a425e60cd714 h1:cqNk8PEwHnK0vqWln+U/YZhQc9h2NB3KjUjDPZo5Q2s= @@ -263,12 +271,14 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/koron/go-ssdp v0.0.6 h1:Jb0h04599eq/CY7rB5YEqPS83HmRfHP2azkxMN2rFtU= github.com/koron/go-ssdp v0.0.6/go.mod h1:0R9LfRJGek1zWTjN3JUNlm5INCDYGpRDfAptnct63fI= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -299,6 +309,9 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI= github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -312,10 +325,15 @@ github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= +github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= +github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= github.com/multiformats/go-multiaddr v0.16.1 h1:fgJ0Pitow+wWXzN9do+1b8Pyjmo8m5WhGfzpL82MpCw= @@ -324,14 +342,22 @@ github.com/multiformats/go-multiaddr-dns v0.4.1 h1:whi/uCLbDS3mSEUMb1MsoT4uzUeZB github.com/multiformats/go-multiaddr-dns v0.4.1/go.mod h1:7hfthtB4E4pQwirrz+J0CcDUfbWzTqEzVyYKKIKpgkc= github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= +github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= +github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= github.com/multiformats/go-multicodec v0.10.0 h1:UpP223cig/Cx8J76jWt91njpK3GTAO1w02sdcjZDSuc= github.com/multiformats/go-multicodec v0.10.0/go.mod h1:wg88pM+s2kZJEQfRCKBNU+g32F5aWBEjyFHXvZLTcLI= +github.com/multiformats/go-multihash v0.0.10/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= +github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= +github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= +github.com/multiformats/go-multihash v0.0.15/go.mod h1:D6aZrWNLFTV/ynMpKsNtB40mJzmCl4jb1alC0OvHiHg= github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= github.com/multiformats/go-multistream v0.6.1 h1:4aoX5v6T+yWmc2raBHsTvzmFhOI8WVOer28DeBBEYdQ= github.com/multiformats/go-multistream v0.6.1/go.mod h1:ksQf6kqHAb6zIsyw7Zm+gAuVo57Qbq84E27YlYqavqw= +github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.1.0 h1:i2wqFp4sdl3IcIxfAonHQV9qU5OsZ4Ts9IOoETFs5dI= github.com/multiformats/go-varint v0.1.0/go.mod h1:5KVAVXegtfmNQQm/lCY+ATvDzvJJhSkUlGQV9wgObdI= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= @@ -346,6 +372,7 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= github.com/polydawn/refmt v0.89.1-0.20231129105047-37766d95467a h1:cgqrm0F3zwf9IPzca7xN4w+Zy6MC9ZkPvAC8QEWa/iQ= github.com/polydawn/refmt v0.89.1-0.20231129105047-37766d95467a/go.mod h1:ocZfO/tLSHqfScRDNTJbAJR1by4D1lewauX9OwTaPuY= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -403,6 +430,7 @@ github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU= github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4= github.com/warpfork/go-testmark v0.12.1 h1:rMgCpJfwy1sJ50x0M0NgyphxYYPMOODIJHhsXyEHU0s= github.com/warpfork/go-testmark v0.12.1/go.mod h1:kHwy7wfvGSPh1rQJYKayD4AbtNaeyZdcGi9tNJTaa5Y= +github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ= github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 h1:5HZfQkwe0mIfyDmc1Em5GqlNRzcdtlv4HTNmdpt7XH0= @@ -452,9 +480,11 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= @@ -606,6 +636,7 @@ golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -619,6 +650,7 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= From 1559d988c0572deef6e826a07e308898ab58d95e Mon Sep 17 00:00:00 2001 From: Chris Warburton Date: Mon, 23 Feb 2026 14:04:04 +0000 Subject: [PATCH 2/3] feat: extracting git trees and blobs --- cmd/car/README.md | 2 +- cmd/car/car.go | 2 +- cmd/car/extract.go | 1 + cmd/car/lib/extract.go | 148 ++++++++++++++++++++++++++++ cmd/car/testdata/script/extract.txt | 8 ++ 5 files changed, 159 insertions(+), 2 deletions(-) diff --git a/cmd/car/README.md b/cmd/car/README.md index 995850fd..e9cdb421 100644 --- a/cmd/car/README.md +++ b/cmd/car/README.md @@ -18,7 +18,7 @@ COMMANDS: create, c Create a car file debug debug a car file detach-index Detach an index to a detached file - extract, x Extract the contents of a car when the car encodes UnixFS data + extract, x Extract the contents of a car when the car encodes UnixFS or Git data filter, f Filter the CIDs in a car get-block, gb Get a block out of a car get-dag, gd Get a dag out of a car diff --git a/cmd/car/car.go b/cmd/car/car.go index 29dd9c10..d15a0482 100644 --- a/cmd/car/car.go +++ b/cmd/car/car.go @@ -102,7 +102,7 @@ func main1() int { { Name: "extract", Aliases: []string{"x"}, - Usage: "Extract the contents of a car when the car encodes UnixFS data", + Usage: "Extract the contents of a car when the car encodes UnixFS or Git data", Action: ExtractCar, ArgsUsage: "[output directory|-]", Flags: []cli.Flag{ diff --git a/cmd/car/extract.go b/cmd/car/extract.go index 91c0c033..d5482e2e 100644 --- a/cmd/car/extract.go +++ b/cmd/car/extract.go @@ -10,6 +10,7 @@ import ( "sync" "github.com/ipfs/go-cid" + _ "github.com/ipfs/go-ipld-git" "github.com/ipld/go-car/cmd/car/lib" "github.com/ipld/go-car/v2" carstorage "github.com/ipld/go-car/v2/storage" diff --git a/cmd/car/lib/extract.go b/cmd/car/lib/extract.go index 51ac9594..70f4fdcf 100644 --- a/cmd/car/lib/extract.go +++ b/cmd/car/lib/extract.go @@ -1,6 +1,7 @@ package lib import ( + "bytes" "context" "errors" "fmt" @@ -54,6 +55,29 @@ func ExtractToDir(c context.Context, ls *ipld.LinkSystem, root cid.Cid, outputDi return 0, nil } + if root.Prefix().Codec == cid.GitRaw { + nd, err := ls.Load(ipld.LinkContext{}, cidlink.Link{Cid: root}, basicnode.Prototype.Any) + if err != nil { + return 0, err + } + var outputResolvedDir string + if outputDir != "-" { + outputResolvedDir, err = filepath.EvalSymlinks(outputDir) + if err != nil { + return 0, err + } + } + if nd.Kind() == ipld.Kind_Bytes { + // Root is a bare git blob; extract it as a single file. + var outputName string + if outputDir != "-" { + outputName = filepath.Join(outputResolvedDir, "unknown") + } + return 1, extractGitBlob(nd, outputName) + } + return extractGitDir(c, ls, nd, outputResolvedDir, "/", path, verbose, logger) + } + pbn, err := ls.Load(ipld.LinkContext{}, cidlink.Link{Cid: root}, dagpb.Type.PBNode) if err != nil { return 0, err @@ -299,3 +323,127 @@ func extractFile(c context.Context, ls *ipld.LinkSystem, n ipld.Node, outputName _, err = io.Copy(f, nlr) return err } + +// extractGitDir extracts a git-raw tree node to a directory, following the +// same path-filtering and output conventions as extractDir. +func extractGitDir(c context.Context, ls *ipld.LinkSystem, n ipld.Node, outputRoot, outputPath string, matchPath []string, verbose bool, logger io.Writer) (int, error) { + if n.Kind() != ipld.Kind_Map { + return 0, ErrNotDir + } + + if outputRoot != "" { + dirPath, err := resolvePath(outputRoot, outputPath) + if err != nil { + return 0, err + } + if err := os.MkdirAll(dirPath, 0755); err != nil { + return 0, err + } + } + + if outputPath == "-" && len(matchPath) == 0 { + return 0, fmt.Errorf("cannot extract a directory to stdout, use a path to extract a specific file") + } + + subPath := matchPath + if len(matchPath) > 0 { + subPath = matchPath[1:] + } + + extractEntry := func(name string, entry ipld.Node) (int, error) { + nextPath := path.Join(outputPath, name) + var nextRes string + if outputRoot != "" { + var err error + nextRes, err = resolvePath(outputRoot, nextPath) + if err != nil { + return 0, err + } + if verbose { + fmt.Fprintf(logger, "%s\n", nextRes) + } + } + + hashNode, err := entry.LookupByString("hash") + if err != nil { + return 0, err + } + link, err := hashNode.AsLink() + if err != nil { + return 0, err + } + child, err := ls.Load(ipld.LinkContext{}, link, basicnode.Prototype.Any) + if err != nil { + if nf, ok := err.(interface{ NotFound() bool }); ok && nf.NotFound() { + fmt.Fprintf(logger, "data for entry not found: %s (skipping...)\n", nextPath) + return 0, nil + } + return 0, err + } + + switch child.Kind() { + case ipld.Kind_Bytes: + return 1, extractGitBlob(child, nextRes) + case ipld.Kind_Map: + return extractGitDir(c, ls, child, outputRoot, nextPath, subPath, verbose, logger) + default: + return 0, fmt.Errorf("unexpected git node kind %s at %s", child.Kind(), nextPath) + } + } + + if len(matchPath) > 0 { + val, err := n.LookupByString(matchPath[0]) + if err != nil { + return 0, err + } + return extractEntry(matchPath[0], val) + } + + var count int + mi := n.MapIterator() + for !mi.Done() { + key, val, err := mi.Next() + if err != nil { + return 0, err + } + ks, err := key.AsString() + if err != nil { + return 0, err + } + ecount, err := extractEntry(ks, val) + if err != nil { + return 0, err + } + count += ecount + } + return count, nil +} + +// extractGitBlob writes the content of a git blob node to outputName (or +// stdout when outputName is ""). Git blob bytes include the object header +// "blob \0", which is stripped before writing. +func extractGitBlob(n ipld.Node, outputName string) error { + b, err := n.AsBytes() + if err != nil { + return err + } + // Strip git object header: "blob \0" + nullIdx := bytes.IndexByte(b, 0) + if nullIdx < 0 { + return fmt.Errorf("invalid git blob: missing null byte in header") + } + content := b[nullIdx+1:] + + var f *os.File + if outputName == "" { + f = os.Stdout + } else { + f, err = os.Create(outputName) + if err != nil { + return err + } + defer f.Close() + } + _, err = f.Write(content) + return err +} diff --git a/cmd/car/testdata/script/extract.txt b/cmd/car/testdata/script/extract.txt index aa2713be..30ba3873 100644 --- a/cmd/car/testdata/script/extract.txt +++ b/cmd/car/testdata/script/extract.txt @@ -93,6 +93,14 @@ stderr -count=1 '^data for entry not found: /zimdump_version \(skipping\.\.\.\)$ stderr -count=1 '^data for entry not found: /favicon.ico \(skipping\.\.\.\)$' stderr -count=1 '^data for entry not found: /index.html \(skipping\.\.\.\)$' +# git-raw CAR extract +mkdir actual-git-raw +car extract -f ${INPUTS}/sample-git-raw.car actual-git-raw +stderr '^extracted 1 file\(s\)$' +cmp actual-git-raw/baz expected-git-raw/baz + +-- expected-git-raw/baz -- +hello -- expected/a/1/A.txt -- a1A -- expected/a/2/B.txt -- From b4034acf38e92e5e7418a613f543f38249b9033c Mon Sep 17 00:00:00 2001 From: Chris Warburton Date: Mon, 23 Feb 2026 17:14:27 +0000 Subject: [PATCH 3/3] feat: extracting git commits and tags Most git users interact with tags and commits rather than trees and blobs, so we should behave sanely when given such IDs. Previously, we were assuming that non-blobs must be trees; which may give confusing errors. To improve that, we must figure out which object type we're dealing with: that requires checking the initial bytes of the block, since the decoded representation is ambiguous (the keys of tree objects are user-provided, and hence may "spoof" the structure of a decoded commit or tag object). Once we've figured out the type of git object we're dealing with, we might as well handle commits and tags correctly (since they're just aliases for other objects, we can follow them until we reach a tree/blob to extract). --- cmd/car/lib/extract.go | 94 ++++++++++++++---- cmd/car/testdata/inputs/sample-git-commit.car | Bin 0 -> 1694 bytes .../testdata/inputs/sample-git-tag-blob.car | Bin 0 -> 255 bytes cmd/car/testdata/inputs/sample-git-tag.car | Bin 0 -> 1864 bytes cmd/car/testdata/script/extract.txt | 38 +++++++ 5 files changed, 110 insertions(+), 22 deletions(-) create mode 100644 cmd/car/testdata/inputs/sample-git-commit.car create mode 100644 cmd/car/testdata/inputs/sample-git-tag-blob.car create mode 100644 cmd/car/testdata/inputs/sample-git-tag.car diff --git a/cmd/car/lib/extract.go b/cmd/car/lib/extract.go index 70f4fdcf..031177ef 100644 --- a/cmd/car/lib/extract.go +++ b/cmd/car/lib/extract.go @@ -56,26 +56,19 @@ func ExtractToDir(c context.Context, ls *ipld.LinkSystem, root cid.Cid, outputDi } if root.Prefix().Codec == cid.GitRaw { - nd, err := ls.Load(ipld.LinkContext{}, cidlink.Link{Cid: root}, basicnode.Prototype.Any) - if err != nil { - return 0, err - } var outputResolvedDir string + var err error if outputDir != "-" { outputResolvedDir, err = filepath.EvalSymlinks(outputDir) if err != nil { return 0, err } } - if nd.Kind() == ipld.Kind_Bytes { - // Root is a bare git blob; extract it as a single file. - var outputName string - if outputDir != "-" { - outputName = filepath.Join(outputResolvedDir, "unknown") - } - return 1, extractGitBlob(nd, outputName) + var blobName string + if outputDir != "-" { + blobName = filepath.Join(outputResolvedDir, "unknown") } - return extractGitDir(c, ls, nd, outputResolvedDir, "/", path, verbose, logger) + return extractGitAnyNode(c, ls, cidlink.Link{Cid: root}, outputResolvedDir, "/", blobName, path, verbose, logger) } pbn, err := ls.Load(ipld.LinkContext{}, cidlink.Link{Cid: root}, dagpb.Type.PBNode) @@ -372,7 +365,7 @@ func extractGitDir(c context.Context, ls *ipld.LinkSystem, n ipld.Node, outputRo if err != nil { return 0, err } - child, err := ls.Load(ipld.LinkContext{}, link, basicnode.Prototype.Any) + cnt, err := extractGitAnyNode(c, ls, link, outputRoot, nextPath, nextRes, subPath, verbose, logger) if err != nil { if nf, ok := err.(interface{ NotFound() bool }); ok && nf.NotFound() { fmt.Fprintf(logger, "data for entry not found: %s (skipping...)\n", nextPath) @@ -380,15 +373,7 @@ func extractGitDir(c context.Context, ls *ipld.LinkSystem, n ipld.Node, outputRo } return 0, err } - - switch child.Kind() { - case ipld.Kind_Bytes: - return 1, extractGitBlob(child, nextRes) - case ipld.Kind_Map: - return extractGitDir(c, ls, child, outputRoot, nextPath, subPath, verbose, logger) - default: - return 0, fmt.Errorf("unexpected git node kind %s at %s", child.Kind(), nextPath) - } + return cnt, nil } if len(matchPath) > 0 { @@ -419,6 +404,71 @@ func extractGitDir(c context.Context, ls *ipld.LinkSystem, n ipld.Node, outputRo return count, nil } +// gitRawType reads the first bytes of a git-raw block to return its type +// string: "blob", "tree", "commit", or "tag". +func gitRawType(ls *ipld.LinkSystem, lnk ipld.Link) (string, error) { + r, err := ls.StorageReadOpener(ipld.LinkContext{}, lnk) + if err != nil { + return "", err + } + buf := make([]byte, 7) // long enough for "commit " (7 bytes) + n, err := r.Read(buf) + if n == 0 { + return "", fmt.Errorf("reading git object header: %w", err) + } + raw := buf[:n] + for _, t := range []string{"blob", "tree", "commit", "tag"} { + prefix := t + " " + if len(raw) >= len(prefix) && string(raw[:len(prefix)]) == prefix { + return t, nil + } + } + return "", fmt.Errorf("unrecognized git object header: %q", raw) +} + +// extractGitAnyNode dispatches a git-raw link to the right handler by reading +// the object-type from its raw block header ("blob", "tree", "commit", "tag"). +// blobPath is the output file path for a bare blob (empty = stdout); dirPath +// is the current relative path within outputRoot when entering a tree. +func extractGitAnyNode(c context.Context, ls *ipld.LinkSystem, lnk ipld.Link, outputRoot, dirPath, blobPath string, matchPath []string, verbose bool, logger io.Writer) (int, error) { + typ, err := gitRawType(ls, lnk) + if err != nil { + return 0, err + } + nd, err := ls.Load(ipld.LinkContext{}, lnk, basicnode.Prototype.Any) + if err != nil { + return 0, err + } + switch typ { + case "blob": + return 1, extractGitBlob(nd, blobPath) + case "commit": + treeNd, err := nd.LookupByString("tree") + if err != nil { + return 0, err + } + treeLnk, err := treeNd.AsLink() + if err != nil { + return 0, err + } + return extractGitAnyNode(c, ls, treeLnk, outputRoot, dirPath, blobPath, matchPath, verbose, logger) + case "tag": + objNd, err := nd.LookupByString("object") + if err != nil { + return 0, err + } + objLnk, err := objNd.AsLink() + if err != nil { + return 0, err + } + return extractGitAnyNode(c, ls, objLnk, outputRoot, dirPath, blobPath, matchPath, verbose, logger) + case "tree": + return extractGitDir(c, ls, nd, outputRoot, dirPath, matchPath, verbose, logger) + default: + return 0, fmt.Errorf("unrecognized git object type: %s", typ) + } +} + // extractGitBlob writes the content of a git blob node to outputName (or // stdout when outputName is ""). Git blob bytes include the object header // "blob \0", which is stripped before writing. diff --git a/cmd/car/testdata/inputs/sample-git-commit.car b/cmd/car/testdata/inputs/sample-git-commit.car new file mode 100644 index 0000000000000000000000000000000000000000..5038716f90e35262a564be9cf442d924f73cc89d GIT binary patch literal 1694 zcmb`HTWl0%6vwB~NEkwhq#}d|M?f0dO`Z8JGxNn}QwwOR2+Adj37Y+8&a?~DopyF; zw~5M9A<-t4Mhplf)Krr`pb%<9tO@7?B1O@}U_z=8KqFF8dyyhh8r_+_Jjf6OVV^en zpE>_?zH@%3q8nVt@siulmAyDmC`vt8@@CJHr_c3`9&Fm7AGmdX<43=k(c-#haFcN- zQS^7wok9$!p&{-`NKpmP1&}NYEQ+bjRZG`YWEd*a5SJ09z*ODx1*&Oaij6TB5}rgj z(i9G0=ql56LW!ISS241%jttc>IA&On5h}*Ec=e7e)jIVFX_*63<)+L((}5~T#TpuI zsK`_jl@yAof+%A|DwmNgi`n`-xce`~HDE(53F2#h@BfUfeeUSZ=E%v?@+s@D)=$iU zmf@gbh6N&Lev%n?g{_k%MH73!nraSzsC>W6xTy6GL<%YrUb_C%+_w(24{RtN8t*$e z)_wZrho&jAtP&zwaT|p6l6Ouln0aYmXY*EB=-H?Acg*}Va1YCych4tKgn6UsjU7L4 zHmiU8GCA$Z*`K)|*QN6+5#ArQKR9#b*DE)swyqjG)Ae56&GpY`YfEn7e(~a*&Won_ zXxZW6xr?qzJ0}N^r1mfNdk@RYZHG&m(9{C1*D3~KVFtOdCz>{GPH{2nXA83ldcm@g zuNDbiw7vHH$m!m9KaEzmeLnKY?#H@U1TJAh&P73(P&Mlk_SFA%!NRTd;Q0K$?cbd( zJ3IXB!-F-(w!K4vOPG*zDUDP#wpwDu?WS$qvE1Q5|VGB^i?AZ#hfiUDW0@v}IK(rT|h!$ufP5y>aa@%6iB>^^1}m*!4P z7Q~4KuO35tq64#P#@D^>T(Z;sYosC~4W<79aN6+e^l?CucOrE)uKs6JXOjO_+)`3~rkQqg{%{*+(_xEP5NW?v{%73Z9HuP88SAT5#_Js?b WPhCsC-Bme#O%^~@c&R(xyng|iu5L2` literal 0 HcmV?d00001 diff --git a/cmd/car/testdata/inputs/sample-git-tag-blob.car b/cmd/car/testdata/inputs/sample-git-tag-blob.car new file mode 100644 index 0000000000000000000000000000000000000000..28ea61caa1dfc6696f81637c3f7f5425b6f92a55 GIT binary patch literal 255 zcmdN}lvl5$;Vy?Z0?tT$$Deaqd&D|@8tBd)S}|d{5-}L zjJTAPB&I7E8k;fXCuOB3mnc{o8z-5YS^x=iGxH<^^VCF>ed1octs%kO^gmx@nm?sUUWGYLS9heny^xOMa??O%{-qlAr33T9KGr zkdvyHoS$o_U}$P&YG`C?X=$pUZD3?zz{M4upPQ-xHbE8S(8<=N&D+bW*B&VoTmbQWTGIdk literal 0 HcmV?d00001 diff --git a/cmd/car/testdata/inputs/sample-git-tag.car b/cmd/car/testdata/inputs/sample-git-tag.car new file mode 100644 index 0000000000000000000000000000000000000000..dbe6ab255c7a99dc4708c7ab4aaf52c79353e74d GIT binary patch literal 1864 zcmb_dYitx{6rDmNaSS0gMIroPM5LkJ)S2%wkJxN#6)Y7+L86!-nQ!LKcFT5WJ3F+y ziO5pHN0Uk;Mg$V7#iT@hgxU~mg8BoIqDW#eAyo)fBT`cP&?-?H-I;y-kRd2>{>&uj z&OP_>o%2=qfMeTka_gDOC+2e{sawlWEnk>Y(1Eu$Y<@01Ty|vT^G$E;jkJQ3jN0*% z10~lAarKBGVVSd86Bw?bs=9(SqL?ZpmZ&pDQYlt7ung0rrfvX{2?mkyZaM*iVK+BN zUEa&lDtWI6I6{Nn7#HrhL8xiUewjAZLP~E=#GpDG9u_2usD!Ad5ur*%qR8`W>}C-1 zmGXc3mA=rs^no+|;|E)|tNX5;UH|qEX1Fv*CLl@XTn9j~EU+l1B9kpuk&$7@NI^_Q zl!7L!mIqKp0h3SY4#Zu7Fr-Kfz))pPQ3)ktHeANY!YVRk!(dp$s!k}cZ*m*$|K3Ng zKKDi@uo_~ZCxPGWZRxMEHIE#=+!{JoQ8i=j#m1>ypl#G|Si>9)8cHWEoItJI3j!gC+oaj06)ZAH;D9VHgRx}1&X6egE z7tJ2t)782~oL#e$>yxxQI zvY5>Tg==X8$4&EoSP(%z?4FjUjVZ>5y=*}=elJ+$3e_U6n|3su9XrwY$_L@P_K(LF zzIs>B3ZEqi$g@ZY6DsFef=2yM7R+7A3{5WR-*)PB<>}FfZXc>Qw(cJBS%QE(OGT(U zk+%4d*GtgC{4fb|^$?5MLYwWxO#bK$FZ#iX1wS8;rk2$@htV(X$G&)fXUDRFepD^5 zABvD-FcTw9!C+{jMN|t>qJV6HBpa&8G{j6=)DN;u)l`T`tgL_)ED=CNWyoL*B!aLd zAxj6QU8SETIZIgOHfFUEWC|g{a|+wTuj3T~8v7r!^Y{6FCZL=UrxrbZ1nmkB&Z(b# z>=}DFmKmt}x*&z3)VOD>8n*2^ms-3d@kxBnO&7NJt{PccL~l%p8@v?;<@MQRTs*wN zLcFfjj{6>4H{I!$PL(CgzpL;2{G;+aC+>cCAb&Ns!21LC`7i(ESH!b7PpvnpKh_P@#4di*{^jH6x|Ux`zSLcF P^Xi-cQE^h&^1QzRtO%nj literal 0 HcmV?d00001 diff --git a/cmd/car/testdata/script/extract.txt b/cmd/car/testdata/script/extract.txt index 30ba3873..6b012a0a 100644 --- a/cmd/car/testdata/script/extract.txt +++ b/cmd/car/testdata/script/extract.txt @@ -99,8 +99,46 @@ car extract -f ${INPUTS}/sample-git-raw.car actual-git-raw stderr '^extracted 1 file\(s\)$' cmp actual-git-raw/baz expected-git-raw/baz +# git-raw CAR rooted at a commit: extracts the commit's tree +mkdir actual-git-commit +car extract -f ${INPUTS}/sample-git-commit.car actual-git-commit +stderr '^extracted 5 file\(s\)$' +cmp actual-git-commit/file expected-git-commit/file +cmp actual-git-commit/dir/f1 expected-git-commit/dir/f1 +cmp actual-git-commit/dir/f4 expected-git-commit/dir/f4 +cmp actual-git-commit/dir/subdir/f2 expected-git-commit/dir/subdir/f2 +cmp actual-git-commit/dir2/f3 expected-git-commit/dir2/f3 + +# git-raw CAR rooted at a tag pointing to a commit: follows tag then extracts tree +mkdir actual-git-tag +car extract -f ${INPUTS}/sample-git-tag.car actual-git-tag +stderr '^extracted 5 file\(s\)$' +cmp actual-git-tag/file expected-git-commit/file +cmp actual-git-tag/dir/f1 expected-git-commit/dir/f1 +cmp actual-git-tag/dir/f4 expected-git-commit/dir/f4 +cmp actual-git-tag/dir/subdir/f2 expected-git-commit/dir/subdir/f2 +cmp actual-git-tag/dir2/f3 expected-git-commit/dir2/f3 + +# git-raw CAR rooted at a tag pointing to a blob: extracts the blob as 'unknown' +mkdir actual-git-tag-blob +car extract -f ${INPUTS}/sample-git-tag-blob.car actual-git-tag-blob +stderr '^extracted 1 file\(s\)$' +cmp actual-git-tag-blob/unknown expected-git-tag-blob/unknown + -- expected-git-raw/baz -- hello +-- expected-git-commit/file -- +Hello world +-- expected-git-commit/dir/f1 -- +qwerty +-- expected-git-commit/dir/f4 -- +;qjkxb +-- expected-git-commit/dir/subdir/f2 -- +123456 +-- expected-git-commit/dir2/f3 -- +',.pyf +-- expected-git-tag-blob/unknown -- +fgcrl -- expected/a/1/A.txt -- a1A -- expected/a/2/B.txt --